boost::multiprecision::mpz_int构造函数继承失败

Constructor inheritance failure with boost::multiprecision::mpz_int

本文关键字:构造函数 继承 失败 int multiprecision mpz boost      更新时间:2023-10-16

我试图创建一个类派生自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)...)
    {}
};

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

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

这似乎是由mpz_int的构造函数的默认参数引起的(mpz_intboost::multiprecision::number的特定实例化的类型定义),这些参数用于SFINAE(例如,给定template <class V>的构造函数接受const V &,如果V满足条件X选择一个构造函数,如果V满足条件Y选择另一个构造函数)。

一个小复制是:

#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) { }
};

仍然得到相同的结果——在c++中是错误的,在clang中是正常的。

现在,问题是这个结构实际上是否应该根据标准被接受。不幸的是,没有明确的答案。§12.9[类。/p1表示

使用using-declaration(7.3.3)隐式命名构造函数的声明一组继承构造函数。的候选集继承了类X的构造函数using-declaration由实际构造函数和由default转换产生的概念构造函数组成参数如下:

  • X的所有非模板构造函数,和
  • 对于X中每个至少有一个带默认实参形参的非模板构造函数,该构造函数集省略任何省略参数说明和结果从末尾开始依次省略带有默认实参的形参参数类型列表
  • X的所有构造函数模板,和
  • 对于X的每个至少有一个带默认实参的形参的构造函数模板,生成的构造函数模板集从省略任何省略参数规范和先后类末尾省略带有默认实参的形参 parameter-type-list

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

如果两个using-declarations用相同的签名,程序是病态的(9.2,13.1),因为一个隐式声明的构造函数由第一个引入using-declaration不是用户声明的构造函数,因此不排除使用相同构造函数的另一个声明使用声明进行后续签名。

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

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