Как получить возвращаемый тип функции-члена из класса?
Следующая программа дает ошибку компиляции с clang
, хотя она передает другие компиляторы:
#include <utility>
struct foo
{
auto bar() -> decltype(0)
{
return 0;
}
using bar_type = decltype(std::declval<foo>().bar());
};
int main()
{
return 0;
}
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::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
Ответы
Ответ 1
g++ 4.9 выдает ту же ошибку
Я не уверен, что это недопустимый код, потому что неполные типы разрешены для declval
, а выражение в decltype
не оценивается.
rightføld в своем ответе объяснил очень хорошо, почему этот код недействителен.
Вы можете использовать std:: result_of:
using bar_type = std::result_of<decltype(&foo::bar)(foo)>::type;
Что на самом деле реализовано так:
using bar_type = decltype((std::declval<foo>().*std::declval<decltype(&foo::bar)>())());
Разница между этим и кодом в вопросе заключается в том, что оператор-оператор-оператор (.*
) используется вместо оператора доступа к члену (.
), и он не требует, чтобы тип был завершен, который демонстрируется этим кодом:
#include <utility>
struct foo;
int main() {
int (foo::*pbar)();
using bar_type = decltype((std::declval<foo>().*pbar)());
}
Ответ 2
В §7.1.6.2 говорится:
Для выражения e
тип, обозначенный символом decltype(e)
, определяется следующим образом:
- if
e
- это несферированное id-выражение или unparenthesized доступ к члену класса (5.2.5), decltype(e)
- это тип объекта с именем e
.... - ...
В §5.2.5 говорится:
Для первого параметра (точка) первое выражение должно иметь полный тип класса....
В §9.2 говорится:
Класс считается полностью определенным типом объекта (3.9) (или полным типом) при закрытии }
спецификатора класса....
decltype(std::declval<foo>().bar())
(и, в свою очередь, std::declval<foo>().bar()
) появляется перед закрытием }
, поэтому foo
является неполным, поэтому std::declval<foo>().bar()
плохо сформирован, поэтому clang является правильным.