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
is
true
decltype(派生的{}.run())的原因
有效。但gcc 4.9.4中的
std::是可构造的
std::是可构造的
的实现使用了
decltype()
准确地知道它是否有默认构造函数?如果我误解了你回答的这一部分,请告诉我。谢谢!@AdvSphere
std::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... }
{}