Как получить возвращаемый тип функции-члена из класса?

Следующая программа дает ошибку компиляции с clang, хотя она передает другие компиляторы:

#include <utility>
struct foo
{ auto bar() -> decltype(0) { return 0; } using bar_type = decltype(std::declval<foo>().bar());
};
int main()
{ return 0;
}
</foo></utility>

clang дает:

$ clang -std=c++11 clang_repro.cpp
clang_repro.cpp:10:48: error: member access into incomplete type 'foo' using bar_type = decltype(std::declval<foo>().bar()); ^
clang_repro.cpp:3:8: note: definition of 'foo' is not complete until the closing '}'
struct foo ^
1 error generated.
</foo>

Является ли эта программа незаконной, и если да, существует ли правильный способ определить foo::bar_type?

clang подробности:

$ clang --version
Ubuntu clang version 3.5-1ubuntu1 (trunk) (based on LLVM 3.5)
Target: x86_64-pc-linux-gnu
Thread model: posix
2 ответа

g++ 4.9 выдает ту же ошибку

Я не уверен, что это недопустимый код, потому что неполные типы разрешены для declval, а выражение в decltype не оценивается.rightføld в своем ответе объяснил очень хорошо, почему этот код недействителен.

Вы можете использовать std:: result_of:

using bar_type = std::result_of<decltype(&foo::bar)(foo)>::type;
</decltype(&foo::bar)(foo)>

Что на самом деле реализовано так:

using bar_type = decltype((std::declval<foo>().*std::declval<decltype(&foo::bar)>())());
</decltype(&foo::bar)></foo>

Разница между этим и кодом в вопросе заключается в том, что оператор-оператор-оператор (.*) используется вместо оператора доступа к члену (.), и он не требует, чтобы тип был завершен, который демонстрируется этим кодом:

#include <utility>
struct foo;
int main() { int (foo::*pbar)(); using bar_type = decltype((std::declval<foo>().*pbar)());
}
</foo></utility>


В §7.1.6.2 говорится:

Для выражения <code>e</code> тип, обозначенный символом <code>decltype(e)</code>, определяется следующим образом:

<ul> <li> if <code>e</code> - это несферированное id-выражение или unparenthesized доступ к члену класса (5.2.5), <code>decltype(e)</code> - это тип объекта с именем <code>e</code>....</li> <li>...</li> </ul>

В §5.2.5 говорится:

Для первого параметра (точка) первое выражение должно иметь полный тип класса....

В §9.2 говорится:

Класс считается полностью определенным типом объекта (3.9) (или полным типом) при закрытии <code>}</code> спецификатора класса....

decltype(std::declval<foo>().bar())</foo> (и, в свою очередь, std::declval<foo>().bar()</foo>) появляется перед закрытием }, поэтому foo является неполным, поэтому std::declval<foo>().bar()</foo> плохо сформирован, поэтому clang является правильным.

licensed under cc by-sa 3.0 with attribution.