【问题标题】:How to get the return type of a member function from within a class?如何从类中获取成员函数的返回类型?
【发布时间】:2014-12-13 15:08:56
【问题描述】:

以下程序会产生带有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

【问题讨论】:

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

标签: c++ clang return-type-deduction


【解决方案1】:

g++4.9 issues the same error

我不确定这是否是无效代码,因为declval 允许不完整的类型,并且不会评估decltype 中的表达式。
rightføld in his answer 解释得很清楚很好,为什么这段代码无效。

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

【讨论】:

  • 它适用于 g++4.9,但遗憾的是 clang++3.4 仍然抱怨“将 'sizeof' 无效应用到不完整类型 'foo'”。
  • 是否有机会使用重载的成员函数来完成这项工作?
  • 更新:std::result_of is deprecated in C++17 and removed in C++20;我们现在必须使用 std::invoke_result:using bar_type = std::invoke_result_t&lt;decltype(&amp;foo::bar)&gt;
【解决方案2】:

§7.1.6.2 说:

对于表达式edecltype(e)表示的类型定义如下:

  • 如果e 是未加括号的id 表达式或未加括号的类成员访问(5.2.5),则decltype(e)e 命名的实体的类型。 …

§5.2.5 说:

对于第一个选项(点),第一个表达式应具有完整的类类型。 …

§9.2 说:

在类说明符的结尾 } 处,类被视为完全定义的对象类型 (3.9)(或完整类型)。 …

decltype(std::declval&lt;foo&gt;().bar())(又是std::declval&lt;foo&gt;().bar())出现在结束}之前,所以foo不完整,所以std::declval&lt;foo&gt;().bar()格式不正确,所以clang是正确的。

【讨论】:

  • 您能解释一下为什么decltype((std::declval&lt;foo&gt;().*std::declval&lt;decltype(&amp;foo::bar)&gt;())()) 有效吗?
  • .*-&gt;* 的语义由 §5.5 [expr.mptr.oper] 而非 §5.2.5 管理,并且 §5.5 不需要完整的类型。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-07
相关资源
最近更新 更多