问题 如何从类中获取成员函数的返回类型?


以下程序产生编译错误 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

10458
2017-10-18 00:15


起源

Visual Studio提供相同的错误(语言略有不同)。我认为你更好的选择是在你的函数声明之前对类型进行别名,然后为你的函数使用别名类型,以及你想到的其他目的。 - Darinth
谢谢你的观点。我想到的实际用例(有 bar 作为具有参数的成员函数模板)可能会排除该策略。 - Jared Hoberock
嗯,如果 bar 指向成员的指针也不起作用的模板。如何在静态成员中进行实际工作并使非静态成员只是一个完美的转发包装器?然后你可以使用 decltype 你想要的静态成员。 - T.C.
是的,这种解决方法可能会这样做。但是,我希望有一个解决方案不涉及太多的间接和代码重复。有点尴尬的是,最直接的尝试不能开箱即用。 - Jared Hoberock
关于这一点对我来说没有意义。您希望在类中生成类型别名到函数模板的返回类型,并且所述函数模板的返回类型由模板参数确定?这在逻辑上不起作用。由于c ++不允许的函数模板的多个实例化,bar_type将同时重新呈现多个类型。 - Darinth


答案:


g ++ 4.9发出相同的错误

我不确定这是否是无效代码,因为允许不完整的类型 declval,表达 decltype 没有评估。
右边的回答 非常好解释为什么这段代码无效。

您可以使用 的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)());
}

9
2017-10-18 00:34



谢谢您的帮助! - Jared Hoberock
它适用于g ++ 4.9,但遗憾的是clang ++ 3.4仍然抱怨“将'sizeof'无效应用于不完整类型'foo'”。 - Peixu Zhu
有没有机会使用重载的成员函数来完成这项工作? - Sebastian


§7.1.6.2说:

表达方式 e,表示的类型 decltype(e) 定义如下:

  • 如果 e 是一个未加密码的id-expression或一个不加括号的类成员访问(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() 是不正确的,所以铿锵是正确的。


6
2017-10-18 00:37



你能解释一下原因吗? decltype((std::declval<foo>().*std::declval<decltype(&foo::bar)>())()) 已验证? - Anton Savin
的语义 .* 和 ->* 受§5.5[expr.mptr.oper]而不是§5.2.5的约束,§5.5不需要完整的类型。 - T.C.