Kyl*_*and 7 c++ templates decltype virtual-destructor c++14
我已经为可选的lazy参数创建了一个标头(在GitHub存储库中也可以看到).(这不是我基于标题的第一个问题.)
我有一个基类模板和两个派生类模板.基类模板有一个protected带有的构造函数static_assert.此构造函数仅由特定的派生类调用.里面的static_assert我使用decltype.
真奇怪的是,内部名称decltype的类型在某种程度上受到我的基类模板中是否存在虚拟析构函数的影响.
这是我的MCVE:
#include <type_traits>
#include <utility>
template <typename T>
class Base
{
protected:
template <typename U>
Base(U&& callable)
{
static_assert(
std::is_same<
typename std::remove_reference<decltype(callable())>::type, T
>::value,
"Expression does not evaluate to correct type!");
}
public:
virtual ~Base(void) =default; // Causes error
virtual operator T(void) =0;
};
template <typename T, typename U>
class Derived : public Base<T>
{
public:
Derived(U&& callable) : Base<T>{std::forward<U>(callable)} {}
operator T(void) override final
{
return {};
}
};
void TakesWrappedInt(Base<int>&&) {}
template <typename U>
auto MakeLazyInt(U&& callable)
{
return Derived<
typename std::remove_reference<decltype(callable())>::type, U>{
std::forward<U>(callable)};
}
int main()
{
TakesWrappedInt(MakeLazyInt([&](){return 3;}));
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果析构函数被注释掉,则编译时没有错误.
目的是callable成为一种类型的表达式U,当使用()运算符调用时,返回类型的东西T.如果没有虚拟析构函数Base,则可以正确评估它; 使用虚拟析构函数,它callabele的类型似乎是Base<T>(据我所知,这是没有意义的).
这是G ++ 5.1的错误消息:
recursive_lazy.cpp: In instantiation of ‘Base<T>::Base(U&&) [with U = Base<int>; T = int]’:
recursive_lazy.cpp:25:7: required from ‘auto MakeLazyInt(U&&) [with U = main()::<lambda()>]’
recursive_lazy.cpp:48:47: required from here
recursive_lazy.cpp:13:63: error: no match for call to ‘(Base<int>) ()’
typename std::remove_reference<decltype(callable())>::type, T
Run Code Online (Sandbox Code Playgroud)
这是Clang ++ 3.7的错误信息:
recursive_lazy.cpp:13:55: error: type 'Base<int>' does not provide a call operator
typename std::remove_reference<decltype(callable())>::type, T
^~~~~~~~
recursive_lazy.cpp:25:7: note: in instantiation of function template specialization
'Base<int>::Base<Base<int> >' requested here
class Derived : public Base<T>
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)
编辑: - =delete复制构造函数也会触发此错误.
PcA*_*cAF 10
问题是当你声明析构函数时,不会声明隐式移动构造函数,因为
(N4594 12.8/9)
如果类X的定义没有显式声明一个移动构造函数,那么当且仅当一个非显式的构造函数被隐式声明为默认的
...
- X没有用户声明的析构函数
Base 有用户声明的析构函数(它默认是无关紧要的).
当MakeLazyInt尝试返回构造Derived对象时,它调用Derived移动构造函数.
Derived隐式声明的移动构造函数不会调用Base移动构造函数(因为它不存在),而是您的模板化Base(U&&)构造函数.
这就是问题,callable参数不包含可调用对象而是包含Base对象,它实际上不包含operator ().
要解决这个问题,只需在里面声明移动构造函数Base:
template <typename T>
class Base
{
protected:
template <typename U>
Base(U&& callable)
{
static_assert(
std::is_same<
typename std::remove_reference<decltype(callable())>::type, T
>::value,
"Expression does not evaluate to correct type!");
}
public:
virtual ~Base(void) =default; // When declared, no implicitly-declared move constructor is created
Base(Base&&){} //so we defined it ourselves
virtual operator T(void) =0;
};
Run Code Online (Sandbox Code Playgroud)