SFINAE 发出"Inheriting constructor does not inherit ellipsis"警告

SFINAE gives "Inheriting constructor does not inherit ellipsis" warning

本文关键字:inherit ellipsis not 警告 constructor 发出 Inheriting SFINAE does      更新时间:2023-10-16

我最近遇到了一个新的警告:

继承

构造函数不继承省略号

我正在尝试管道

Object{42}; // ... into an init that handles integers

。和。。。

Object{3.14}; // ... into an init that handles FLOATS

。通过使用一些SFINAE的狡猾:

    #define DECAY(T)                typename std::decay<T>::type
    #define IS_INTEGRAL(T)          std::is_integral< DECAY(T) >::value
    #define IS_FLOATING(T)          std::is_floating_point< DECAY(T) >::value
    #define SUBFAIL_UNLESS(PRED)    typename X = 
                                          typename std::enable_if<PRED>::type
    // long, float
    template<typename T, SUBFAIL_UNLESS(IS_INTEGRAL(T)) > 
    explicit Object( T&& t      ) : Object{ pyob_from_integral(t) }  { }
    template<typename T, SUBFAIL_UNLESS(IS_FLOATING(T)) > 
    explicit Object( T&& t, ... ) : Object{ pyob_from_floating(t) }  { }
private:
    template<typename T> PyObject* pyob_from_integral( T t ) { 
        cout << "integral"; return nullptr; 
    }
    template<typename T> PyObject* pyob_from_floating(T t)   { 
        cout << "FLOATING"; return nullptr; 
    }

. 演示警告的完整代码可以在 coliru 看到。

现在的目的...是为了避免编译器抛出错误,因为它认为我有两个单独的模板模板构造函数。也就是说,意识到它们是不相交/相互排斥的还不够聪明。

此警告的意义是什么,以及如何围绕它进行编码?

PS 请注意,有充分的理由不简单地提供几个构造函数重载;我故意简化场景,使问题尽可能清晰。

编辑:工作简化的测试用例证明了...这里。(即,如果您删除它,它将不再编译(。

OP 试图通过引入省略号来解决的问题 ... ,据我所知,您不能定义具有相同签名的两个函数(违反 ODR(。

签名的这一概念扩展到函数模板。但是,函数模板的签名包括其模板参数。来自最近的 github 草案,基于 N4296:

[defns.signature.templ]

签名

<函数模板> 名称、参数类型列表 (8.3.5(、封闭命名空间(如果有(、 返回类型和模板参数列表

[basic.link]/9.4 和 [temp.over.link] 指定在什么情况下两个函数模板是等效的。在 OP 的情况下,您可以通过稍微改变 SFINAE 的应用方式来使这些函数模板的签名有所不同:

template<typename T,
         typename std::enable_if< std::is_integral<T>::value, int >::type = 0>
explicit Object(T&&);
template<typename T,
         typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
explicit Object(T&&);

现场示例

第二个(非类型(模板参数的类型是依赖于第一个模板参数的表达式。这些表达式区分了这两个模板;有关详细信息,请参阅 [temp.over.link]/5。在 OP 中,第二个模板参数只是一个类型。因此,这两个模板的模板参数是等效的,只是默认模板参数有所不同,它不是签名的一部分。


使用构造函数继承时,省略号的

问题是继承的构造函数发出省略号。这是构造函数继承的一个怪癖,如 [class.inhctor]/1.4 中所述 在 OP 中继承两个 ctor 时,这会导致继承构造函数候选集中有两个具有相同签名的函数模板。我不明白标准中如何解决这个问题;clang++ 和 G++ 同意这是非法的,CTOR"不能继承"或"不能重载"。

现场示例

当基类具有省略号运算符时,它在继承的运算符中被忽略(当使用...继承时(:

class A {
public:
    A(int,...);
};
class B: public A {
public:
    using A::A;
};

在示例中,类 B 有一个 B::B(int(,它将调用 A::A(int,...( ...等于 nothing,因为它没有默认参数值,但默认值是构造函数继承的唯一和强制性选项,请参阅标准的 12.9.1:

"对于至少有一个带有默认参数的 X 的非模板构造函数,省略任何省略号参数规范并连续省略参数和参数类型列表末尾的默认参数的参数而产生的构造函数集,以及">......"对于至少有一个带有默认参数的 X 构造函数模板,通过省略任何省略号参数规范并连续省略参数类型列表末尾的默认参数而生成的构造函数模板集。">

我认为您不能继承带有 ...的构造函数,

在这种情况下,您也不能重新定义它,因为它会与继承的构造函数冲突。