[英]Defining member variable using decltype on variadic function template of a class template
[英]decltype() variadic template base class
我有以下代码,我希望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;
}
我知道你可以使用declval<>
来获取成员函数,而无需通过构造函数,但我的问题是为什么decltype
在这里工作。 我试图在C ++标准中找到相关的东西,但没有找到任何东西。 还尝试了多个编译器(gcc 5.2,7.1和clang 3.8)并具有相同的行为。
看到无价值背景的魔力......和说谎。
实际上尝试做类似的事情:
Derived d;
将是一个编译错误。 这是一个编译器错误,因为在评估Derived::Derived()
的过程中,我们必须调用Base::Base()
,它不存在。
但这是构造函数实现的细节。 在评估的背景下,我们当然需要知道这一点。 但在未评估的背景下,我们不需要走得那么远。 如果你检查std::is_constructible<Derived>::value
,你会发现这是true
! 这是因为您可以在没有参数的情况下实例化该构造函数 - 因为该构造函数的实现超出了该实例化的直接上下文。 这个谎言 - 你可以默认构造Derived
- 允许你在这个上下文中使用Derived{}
,编译器很乐意让你继续你的快乐方式,看看decltype(Derived{}.run())
是int
(这也不涉及实际调用run
,因此该函数的主体同样无关紧要)。
如果您在Derived
构造函数中诚实:
template <typename ...Args,
std::enable_if_t<std::is_constructible<Base, Args&...>::value, int> = 0>
Derived(Args&... args) : Base(args...) { }
然后decltype(Derived{}.run())
将无法编译,因为现在Derived{}
即使在未评估的上下文中也是不正确的。
避免欺骗编译器是件好事。
当decltype
的表达式涉及函数模板时,编译器仅查看模板函数的签名,以确定如果表达式确实在评估的上下文中,是否可以实例化模板。 此时不使用该函数的实际定义。
(事实上,这就是为什么std::declval
可以在decltype
使用的原因,即使std::declval
根本没有定义。)
您的模板构造函数具有相同的签名,就像简单声明但尚未定义一样:
template <typename ...Args>
Derived(Args&... args);
在处理decltype
,编译器只查看那么多信息并确定Derived{}
是一个有效的表达式,一个Derived<>
类型的右值。 : Base{args...}
部分是模板定义的一部分,不会在decltype
。
如果你想在那里编译错误,你可以使用这样的东西使你的构造函数更“SFINAE友好”,这意味着关于模板的特化是否实际有效的信息被放入模板签名中。
template <typename ... Args, typename Enable =
std::enable_if_t<std::is_constructible<Base, Args&...>::value>>
Derived( Args& ... args ) : Base{ args... }
{}
您可能还想修改构造函数以避免“过于完美的转发”。 如果你做Derived x; Derived y{x};
Derived x; Derived y{x};
,模板专业化Derived(Derived&);
将是一个比隐式Derived(const Derived&);
更好的匹配Derived(const Derived&);
,你最终会尝试将x
传递给Base{x}
而不是使用Derived
的隐式复制构造函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.