Mor*_*enn 15 c++ crtp c++11 return-type-deduction c++14
我最近在玩CRTP的时候遇到的东西让我感到惊讶,因为它与c ++ 1y函数一起使用,其类型是推导出来的.以下代码有效:
template<typename Derived>
struct Base
{
auto foo()
{
return static_cast<Derived*>(this)->foo_impl();
}
};
struct Derived:
public Base<Derived>
{
auto foo_impl()
-> int
{
return 0;
}
};
int main()
{
Derived b;
int i = b.foo();
(void)i;
}
Run Code Online (Sandbox Code Playgroud)
我假设返回类型Base<Derived>::foo
是decltype
返回的表达式的一个,但如果我修改这样的函数foo
:
auto foo()
-> decltype(static_cast<Derived*>(this)->foo_impl())
{
return static_cast<Derived*>(this)->foo_impl();
}
Run Code Online (Sandbox Code Playgroud)
此代码不再起作用,我收到以下错误(来自GCC 4.8.1):
||In instantiation of 'struct Base<Derived>':|
|required from here|
|error: invalid static_cast from type 'Base<Derived>* const' to type 'Derived*'|
||In function 'int main()':|
|error: 'struct Derived' has no member named 'foo'|
Run Code Online (Sandbox Code Playgroud)
我的问题是:为什么不起作用?我可以写什么来获得正确的返回类型而不依赖于自动返回类型扣除?
而且,嗯......这是一个现实的例子.
dyp*_*dyp 12
类模板的成员函数的定义仅在odr-used(或显式实例化)时被隐式实例化.也就是说,通过派生Base<Derived>
,您不会隐式实例化函数体.因此,还没有推断出返回类型.
在(*)实例化点,Derived
完成,Derived::foo_impl
声明,返回类型推断可以成功.
(*)不是"the",而是"某些实例化点".有几个.
我假设返回类型
Base<Derived>::foo
是decltype
返回的表达式的一个,但是如果我foo
像这样修改函数:
所述尾返回类型是成员函数的声明的一部分; 因此,它是周围类定义的一部分,需要在派生时进行实例化Base<Derived>
.此时,Derived
仍然是不完整的,具体Derived::foo_impl
尚未宣布.
我可以写什么来获得正确的返回类型而不依赖于自动返回类型扣除?
现在这很棘手.我会说标准中没有明确定义,例如看到这个问题.
这是一个演示clang ++ 3.4没有找到Derived
内部成员的例子Base<Derived>
:
template<typename Derived>
struct Base
{
auto foo() -> decltype( std::declval<Derived&>().foo_impl() )
{
return static_cast<Derived*>(this)->foo_impl();
}
};
Run Code Online (Sandbox Code Playgroud)
declval
不需要完整的类型,所以错误消息是,有没有foo_impl
在Derived
.
有一个黑客,但我不确定它是否合规:
template<typename Derived>
struct Base
{
template<class C = Derived>
auto foo() -> decltype( static_cast<C*>(this)->foo_impl() )
{
static_assert(std::is_same<C, Derived>{}, "you broke my hack :(");
return static_cast<Derived*>(this)->foo_impl();
}
};
Run Code Online (Sandbox Code Playgroud)