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

时间:2014-07-23 13:49:02

标签: c++ c++11 boost language-lawyer inheriting-constructors

我尝试创建一个派生自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给了我the following error

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

有人可以解释为什么第一个例子会产生错误吗?我认为继承基类构造函数并将值转发给它们大致相同。我想我错了,但我仍然有兴趣了解其中的差异。

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

1 个答案:

答案 0 :(得分:9)

这似乎是由the default parameters of mpz_int's constructors引起的(mpz_intboost::multiprecision::number的特定实例化的typedef),用于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() { }

compiles in clang but not g++,产生相同的错误。 (值得注意的是,虽然clang编译上面的repro代码,it doesn't actually work如果你试图使用带有单个参数的继承构造函数,这几乎同样糟糕。你可以使它在clang中工作,但是,{ {3}}。)

我们甚至可以通过简单地使用explicitly supplying the second parameter

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

仍然得到相同的结果 - g ++中出错,铿锵就行。

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

  

using-declaration (7.3.3)隐式命名构造函数   声明一组继承构造函数候选集   继承的构造函数来自于{。}}中命名的类    using-declaration 包含由默认转换产生的实际构造函数和名义构造函数   参数如下:

     
      
  • X
  • 的所有非模板构造函数   
  • X的每个非模板构造函数,它至少有一个带有默认参数的参数,一组构造函数,   省略任何省略号参数规范和结果   从末尾开始用默认参数相继省略参数    parameter-type-list
  •   
  • X
  • 的所有构造函数模板   
  • X的每个构造函数模板,它至少有一个带有默认参数的参数,结果是一组构造函数模板   从省略任何省略号参数规范和连续   使用默认参数从结尾省略参数   参数类型列表
  •   

问题是标准实际上没有指定如果连续省略参数和默认参数过程导致两个具有相同签名的构造函数会发生什么。 (请注意,对于上面X的两个模板构造函数,省略使用default参数的参数会给出签名foo。)虽然第7段有一个说明

  

如果两个 using-declarations 声明继承构造函数   同样的签名,该程序是不正确的(9.2,13.1),因为一个   由第一个引入的隐式声明的构造函数    using-declaration 不是用户声明的构造函数,因此不排除具有相同构造函数的另一个声明   后续使用声明签名。

这里我们只有一个 using-declaration ,因此该注释不适用,并且,虽然确实禁止重复声明,但可以说是对集的引用表示将重复签名简单地视为一个,因此单个 using-declaration 不会引入重复声明。

此问题实际上是针对该标准的两个缺陷报告的主题:skip the templateness for foo's constructorsCWG 1645,目前尚不清楚这些缺陷报告将如何解决。在CWG问题1645的2013年注释中指出的一种可能性是删除这些继承的构造函数(来自多个基础构造函数),因此它们仅在使用时才会导致错误。 CWG问题1941中提出的另一种方法是使继承构造函数的行为与引入类的其他基类函数一样。