C++ decltype()可变模板基类
我有下面的代码,我希望C++ decltype()可变模板基类,c++,c++11,c++14,C++,C++11,C++14,我有下面的代码,我希望decltype()不处理Derived类以获取run()基类方法返回类型,因为基类没有默认构造函数 class Base { public: int run() { return 1; } protected: Base(int){} }; struct Derived : Base { template <typename ...Args> Derived(Args... args) : Base{args...}
decltype()
不处理Derived
类以获取run()
基类方法返回类型,因为基类没有默认构造函数
class Base
{
public:
int run() { return 1; }
protected:
Base(int){}
};
struct Derived : Base
{
template <typename ...Args>
Derived(Args... args) : Base{args...}
{}
};
int main()
{
decltype(Derived{}.run()) v {10}; // it works. Not expected since
// Derived should not have default constructor
std::cout << "value: " << v << std::endl;
//decltype(Base{}.run()) v1 {10}; // does not work. Expected since
// Base does not have default constructor
//std::cout << "value: " << v1 << std::endl;
}
类基
{
公众:
int run(){return 1;}
受保护的:
基(int){}
};
派生结构:基
{
模板
派生(Args…Args):基{Args…}
{}
};
int main()
{
decltype(派生的{}.run())v{10};//它可以工作。自
//派生的构造函数不应具有默认构造函数
看一看未经评估的环境的魔力…和谎言
实际上,我试图做一些事情,比如:
Derived d;
将是一个编译错误。这是一个编译器错误,因为在计算Derived::Derived()
的过程中,我们必须调用Base::Base()
,它不存在
但这是构造函数实现的一个细节。在已评估的上下文中,我们当然需要知道这一点。但在未评估的上下文中,我们不需要走这么远。如果您检查std::is_constructible::value
,您会看到这是true
!这是因为您可以无参数实例化该构造函数-因为该构造函数的实现不在该实例化的直接上下文中。这个谎言——您可以默认构造Derived
——允许您在此上下文中使用Derived{}
,编译器将很高兴地允许您继续快乐的生活,并看到decltype(Derived{}.run())
是int
(这也不涉及实际调用run
,因此该函数的主体也不相关)
如果您在派生的构造函数中诚实:
template <typename ...Args,
std::enable_if_t<std::is_constructible<Base, Args&...>::value, int> = 0>
Derived(Args&... args) : Base(args...) { }
模板
派生(Args&…Args):基(Args…{}
然后decltype(派生的{}.run())
将无法编译,因为现在Derived的{}
即使在未计算的上下文中也是格式错误的
最好避免对编译器撒谎。当decltype
中的表达式涉及函数模板时,编译器只查看模板函数的签名,以确定如果表达式确实在计算上下文中,是否可以实例化模板。函数的实际定义为在那一点上没有使用
(事实上,这就是为什么std::declval
可以在decltype
中使用,即使std::declval
没有任何定义。)
您的模板构造函数具有与仅声明但尚未定义的签名相同的签名:
template <typename ...Args>
Derived(Args&... args);
您可能还希望修改构造函数以避免“过于完美的转发”。如果您执行Derived x;Derived y{x};
,则模板专用化Derived(Derived&);
将比隐式Derived(const-Derived&);
更好地匹配,并且您将尝试将x
传递到Base{x}
与其使用派生的隐式复制构造函数
可能与decltype
是未计算的上下文有关,因此它可以看到派生的
有一个构造函数,它不接受任何参数,但还不至于调用初始值设定项列表?大家回答得很好!非常感谢h你的时间和C++标准参考!我在帖子里找的。我以前尝试过你的解决方案,它就像你提到的一样工作。我仍然期待从编译器看到一个错误(或者至少警告)。<代码>:ST::ISSDEFAUTTHYORGTIOBULL()/<代码>是代码>真的< /代码>。难道这不需要在标准中更清楚吗?(至少提到一行)在编译时类型中进行计算时?@AdvSphere我认为最相关的标准语句是[temp.inst]/4“除非函数模板专门化已显式实例化或显式专门化,否则在需要函数定义存在的上下文中引用专门化时,函数模板专门化将隐式实例化。”在未计算上下文中的用法,如在decltype(expr)中
不是odr用法,也不需要函数定义。@AdvSphere“即时上下文”不是真的。@Barry,正如我今天再次读到你的答案一样,你说的似乎是因为std::is_constructible::value
istrue
是decltype(派生的{}.run())的原因
有效。但gcc 4.9.4中的std::是可构造的
和std::是可构造的
的实现使用了decltype()
准确地知道它是否有默认构造函数?如果我误解了你回答的这一部分,请告诉我。谢谢!@AdvSpherestd::is\u default\u constructible::value
为真是结果,而不是原因。原因是构造函数模板没有约束。
template <typename ... Args, typename Enable =
std::enable_if_t<std::is_constructible<Base, Args&...>::value>>
Derived( Args& ... args ) : Base{ args... }
{}