2016-02-08 3 views
5

Я недавно задал вопрос здесь (Detecting instance method constexpr with SFINAE), где я пытался выполнить некоторое обнаружение constexpr во время компиляции. В конце концов, я понял, что для этого можно использовать noexcept: любое постоянное выражение также равно noexcept. Поэтому я собрал следующую технику:Constexpr decltype

template <class T> 
constexpr int maybe_noexcept(T && t) { return 0; } 
... 
constexpr bool b = noexcept(maybe_noexcept(int{})); 

Это работает и b верно, как и следовало ожидать, как нуль-инициализации int является константным выражением. Он также корректно возвращает нуль, если это необходимо (если я изменю int на другой подходящий тип).

Далее, я хотел проверить, есть ли что-то constexpr двигаться конструктивно. Так что я сделал это:

constexpr bool b = noexcept(maybe_noexcept(int(int{}))); 

И опять же, это работает должным образом для int, или определенного пользователем типа. Однако это проверяет, что тип имеет как конструктор constexpr по умолчанию, так и конструктор перемещения constexpr. Таким образом, чтобы решить эту проблему, я попытался изменить к declval:

constexpr bool b = noexcept(maybe_noexcept(int(declval<int>()))); 

Это приводит к b ложна в GCC 5.3.0 (не может использовать лязг для любого из этого, потому что лязг не правильно сделать постоянной выражения noexcept). Нет проблем, я говорю, должно быть потому, что declval (интересно) не отмечено constexpr. Итак, я пишу свою собственную наивную версию:

template <class T> 
constexpr T&& constexpr_declval() noexcept; 

Да, это наивно по сравнению с тем, как стандартная библиотека делает это, как он будет задыхаться от пустоты и, вероятно, других вещей, но это нормально сейчас. Поэтому я стараюсь снова:

constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>()))); 

Это все еще не работает, b всегда ложно. Почему это не считается постоянным выражением? Является ли это ошибкой компилятора, или я не понимаю фундаментальных данных о constexpr? Похоже, есть какое-то странное взаимодействие между constexpr и неоценимыми контекстами.

+1

@Cameron Вторая часть того, что вы сказали, конечно, верна, но первая технически - нет. Каждое постоянное выражение * является * '' noexcept'', но это не так. Однако возврат функции '' constexpr'' не всегда является постоянным выражением. –

+1

Ваш вызов функции не является постоянным выражением, потому что он не определен ([expr.const] /2.3) – 0x499602D2

ответ

6

constexpr Должны быть определены выражения. Ваш не определен, поэтому в этом случае int(constexpr_declval<int>()) не является constexpr.

Это означает, что maybe_noexcept(int(constexpr_declval<int>())) не является constexpr, то есть не noexcept.

И компилятор правильно возвращает false.

Вы также не можете вызывать UB в constexpr.

Я не могу придумать способ сделать ссылку на произвольные данные constexpr. Я думал, что буфер выравниваемого хранилища переинтерпретируется как ссылка на тип данных, но это UB во многих контекстах, следовательно, не constexpr.

В общем, это невозможно. Представьте, что вы имели класс, состояние которого определяет, будет ли вызов метода является constexpr:

struct bob { 
    int alice; 
    constexpr bob(int a=0):alice(a) {} 
    constexpr int get() const { 
    if (alice > 0) throw std::string("nope"); 
    return alice; 
    } 
}; 

сейчас, bob::getconstexpr или нет? Это если у вас есть constexpr bob, построенный с неположительным alice и ...это не так.

Вы не можете сказать «притворитесь, что это значение constexpr и скажите, если какое-то выражение constexpr». Даже если бы вы могли, это не решило бы проблему вообще, потому что состояние параметра constexpr может измениться, если выражение равно constexpr или нет!

Еще веселее, bob().get()is constexpr, а bob(1).get() нет. Таким образом, ваша первая попытка (по умолчанию построить тип) даже дала неверный ответ: вы можете протестировать, затем выполнить действие, и действие завершится неудачно.

Объект является эффективным параметром метода и без состояния всех параметров, вы не можете определить, является ли функция constexpr.

Способ определения, является ли выражение constexpr, состоящим в том, чтобы запустить его в контексте constexpr и посмотреть, работает ли он.

+0

Так что кажется, что на самом деле просто невозможно правильно проверить, существуют ли методы класса constexpr для класса, потому что вам нужно получить экземпляр для этого, и нет способа получить экземпляр constexpr, который фактически определен и не предполагает существование какого-либо конструктора. Является ли это справедливым описанием ситуации? –

+1

@NirFriedman Ну, вы можете передать аргументы для построения указанного объекта. ;) В общем случае 'constexpr' зависит от контекста - вам нужно знать почти * все * о вызове и его параметрах. Одним из параметров является объект *, выполняющий вызов *: если это не 'constexpr', выражение (вообще) не является. Высказывание «если притворяться, что этот аргумент« constexpr », даже если это не то, что происходит,« не поддерживается, насколько я знаю. – Yakk

+0

Ха-ха действительно. Хорошо, ваш ответ + комментарий был очень информативным, спасибо. –

Смежные вопросы