Что такое decltype с двумя аргументами?

Изменить, чтобы избежать путаницы: decltype не принимает два аргумента. См. Ответы.

Следующие две структуры могут использоваться для проверки существования функции-члена в типе T во время компиляции:

// Non-templated helper struct:
struct _test_has_foo {
    template<class T>
    static auto test(T* p) -> decltype(p->foo(), std::true_type());

    template<class>
    static auto test(...) -> std::false_type;
};

// Templated actual struct:
template<class T>
struct has_foo : decltype(_test_has_foo::test<T>(0))
{};

Я думаю, что идея заключается в использовании SFINAE при проверке существования функции-члена, поэтому в случае, если p->foo() недействителен, определена только версия эллипсов test, которая возвращает std::false_type. В противном случае первый метод будет определен для T* и вернет std::true_type. Фактический "переключатель" происходит во втором классе, который наследуется от типа, возвращаемого test. Это кажется умным и "легким" по сравнению с разными подходами с is_same и тому подобным.

decltype с двумя аргументами сначала выглядел удивительно для меня, так как я думал, что он просто получает тип выражения. Когда я увидел код выше, я подумал, что это похоже на "попытаться скомпилировать выражения и всегда возвращать тип второго. Сбой, если выражения не скомпилируются" (так что скрыть эту специализацию: SFINAE).

Но:

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

...
    template<class T>
    static auto test(T* p) -> decltype(bar(*p), std::true_type());
...

http://ideone.com/dJkLPF

Это, как я думал, вернет a std::true_type тогда и только тогда, когда определено bar, принимающее T в качестве первого параметра (или если T является конвертируемым и т.д.), то есть: если bar(*p) будет компилироваться, если он был написан в некотором контексте, где p определяется типа T*.

Однако приведенная выше модификация всегда оценивается до std::false_type. Почему это? Я не хочу исправлять его с помощью некоторого сложного кода. Я просто хочу знать, почему это не работает, как я ожидал. Ясно, что decltype с двумя аргументами работает не так, как я думал. Я не мог найти никакой документации; это объясняется только одним выражением.

Ответы

Ответ 1

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

Ответ 2

decltype не принимает два аргумента. Просто он может иметь выражение как свой аргумент, а оператор запятой - один из способов создания выражений. В пункте 5.18/1:

[...] Пара выражений, разделенных запятой, оценивается слева направо; левое выражение - это отброшенное значение выражения (пункт 5). Каждое вычисление значения и побочный эффект, связанный с левым выражением секвенируется перед вычислением каждого значения и побочным эффектом, связанным с правильным выражением. Тип и значение результата - это тип и значение правого операнда; результат имеет одну и ту же категорию значений как его правый операнд, и является битовым полем, если его правый операнд является glvalue и бит-полем. Если значение права операнд является временным (12.2), результатом является временный.

Таким образом:

static_assert(std::is_same<decltype(42, 3.14), double>::value, "Will not fire");