13

我试图创建一个派生自boost::multiprecision::mpz_int并让它继承基类构造函数的类:

#include <boost/multiprecision/gmp.hpp>

using namespace boost::multiprecision;

struct Integer:
    mpz_int
{
    using mpz_int::mpz_int;
};

g++ 4.9.0 给了我以下错误

main.cpp:8:20: error: 'template<class tag, class Arg1, class Arg2, class Arg3, class Arg4> Integer::Integer(const boost::multiprecision::detail::expression<tag, Arg1, Arg2, Arg3, Arg4>&)' inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
     using mpz_int::mpz_int;
                    ^
main.cpp:8:20: error: conflicts with version inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: 'template<class Other, boost::multiprecision::expression_template_option ET> Integer::Integer(const boost::multiprecision::number<Backend, ExpressionTemplates>&)' inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: conflicts with version inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: 'template<class Other, boost::multiprecision::expression_template_option ET> Integer::Integer(const boost::multiprecision::number<Backend, ExpressionTemplates>&)' inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: conflicts with version inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: 'template<class V> Integer::Integer(const V&)' inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: conflicts with version inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: 'template<class V> constexpr Integer::Integer(const V&)' inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: conflicts with version inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: 'template<class V> Integer::Integer(const V&)' inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'
main.cpp:8:20: error: conflicts with version inherited from 'boost::multiprecision::number<boost::multiprecision::backends::gmp_int>'

事实是,我不知道为什么会这样。以下解决方法可以实现我想要做的事情:

struct Integer:
    mpz_int
{
    template<typename... Args>
    Integer(Args&&... args):
        mpz_int(std::forward<Args>(args)...)
    {}
};

谁能解释为什么第一个示例会产生错误?我认为继承基类构造函数并将值转发给它们做了大致相同的事情。我想我错了,但我仍然想知道其中的区别。

编辑:我会把事情说清楚。我根本不在乎是否有更好的方法来实现这一点(有很多)。我唯一问的是为什么在这种情况下构造函数继承失败。是由于编译器错误还是标准中某处的一些晦涩规则?

4

1 回答 1

9

这似乎是由用于 SFINAE的构造函数的默认参数mpz_intmpz_int是 的特定实例化的 typedef )引起的(例如,给定构造函数采用 a ,如果满足条件 X则选择一个构造函数,如果满足条件则选择另一个构造函数满足标准 Y)。boost::multiprecision::numbertemplate <class V>const V &VV

一个小的复制品是:

#include <type_traits>
struct foo {
    template<class T>
    foo(T , typename std::enable_if<std::is_integral<T>::value>::type * = nullptr) { }
    template<class T>
    foo(T , typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr) { }
};

struct bar : foo {
    using foo::foo;
};

int main() { }

在 clang 但不是 g++ 编译,产生相同的错误。(值得注意的是,虽然 clang 编译了上面的 repro 代码,但如果您尝试使用带有单个参数的继承构造函数,它实际上并不能工作,这几乎同样糟糕。但是,您可以使其在 clang 中工作,通过显式提供第二个参数。)

我们甚至可以通过简单地使用来跳过foo构造函数的模板性:

struct foo {
    foo(double, int = 0) { }
    foo(double, double = 0) { }
};

并且仍然得到相同的结果 - g++ 中的错误,clang 中的 OK。

现在,问题是这个结构是否应该根据标准被接受。不幸的是,没有明确的答案。§12.9 [class.inhctor]/p1 说

命名构造函数的using-declaration (7.3.3) 隐式声明了一组继承构造函数从using 声明中命名 的类中继承的候选构造函数集由实际构造函数和由默认参数转换产生的概念构造函数组成,如下所示:X

  • X和的所有非模板构造函数
  • X对于具有至少一个带默认参数的参数的每个非模板构造函数,由省略任何省略号参数规范并从parameter-type-list末尾连续省略带默认参数的参数而产生的一组构造函数,以及
  • 的所有构造函数模板X,以及
  • 对于它的每个构造函数模板,X至少有一个带有默认参数的参数,一组构造函数模板是由于省略任何省略号参数规范并从 parameter-type-list的末尾连续省略带有默认参数的参数而产生的。

问题在于,如果这个连续省略参数和默认参数的过程导致两个具有相同签名的构造函数,该标准实际上并没有具体说明会发生什么。(请注意,对于foo上面的两个模板构造函数,省略带有默认参数的参数会给出签名template<class T> foo(T);。)虽然第 7 段有一个注释说

如果两个using-declarations声明具有相同签名的继承构造函数,则程序是非良构的 (9.2, 13.1),因为第一个 using-declaration引入的隐式声明的构造函数不是用户声明的构造函数,因此不排除通过随后的using-declaration对具有相同签名的构造函数的另一个声明。

这里我们只有一个using-declaration,因此该注释不适用,并且,虽然确实禁止重复声明,但可以说第 1 段中对集合的引用意味着重复签名将被简单地视为一个,所以单个using 声明不会引入重复声明。

这个问题实际上是针对标准的两个缺陷报告的主题:CWG 1645CWG 1941,目前还不清楚这些缺陷报告将如何解决。在 CWG 问题 1645 中的 2013 年注释中指出的一种可能性是删除此类继承的构造函数(来自多个基本构造函数),以便它们仅在使用时才会导致错误。CWG issue 1941 中建议的另一种方法是使继承构造函数的行为类似于引入派生类的其他基类函数。

于 2014-09-04T04:02:35.427 回答