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 に使用されるのコンストラクター(の特定のインスタンス化の typedef ) の既定のパラメーターmpz_intが原因であると思われます (たとえば、 を受け取るコンストラクターが与えられた場合、 が基準 X を満たす場合は 1 つのコンストラクターを選択し、基準 X を満たす場合は別のコンストラクターを選択します)。基準 Y を満たす)。mpz_intboost::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 は上記の再現コードをコンパイルしますが、継承されたコンストラクターを単一の引数で使用しようとすると、実際には機能しないことに注意してください。これは、ほぼ同じように悪いことです。ただし、clang で機能させることもできます。 2 番目のパラメーターを明示的に指定します。)

代わりに単純に使用することで、のコンストラクターのテンプレート性をスキップするfooこともできます。

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

それでも同じ結果が得られます-g ++ではエラー、clangではOKです。

さて、問題は、この構造が実際に標準に従って受け入れられるべきかどうかです。残念ながら、明確な答えはありません。§12.9 [class.inhctor]/p1 は、

コンストラクターに名前を付けるusing 宣言(7.3.3) は、継承コンストラクターのセットを暗黙的に宣言します。using 宣言で指定され たクラスから継承されたコンストラクターの候補セットは、実際のコンストラクターと、次のように既定のパラメーターを変換した結果の概念的なコンストラクターで構成されます。X

  • X、およびのすべての非テンプレート コンストラクタ
  • デフォルト引数を持つ少なくとも 1 つのパラメーターを持つの各非テンプレート コンストラクターに対して、省略記号パラメーターの指定を省略し、 parameter-type-listXの末尾からデフォルト引数を持つパラメーターを連続して省略した結果のコンストラクターのセット、および
  • X、およびのすべてのコンストラクタ テンプレート
  • デフォルトの引数を持つ少なくとも 1 つのパラメーターを持つの各コンストラクター テンプレートに対して、省略記号パラメーターの指定を省略し、 parameter-type-listXの末尾からデフォルトの引数を持つパラメーターを連続して省略した結果のコンストラクター テンプレートのセット 。

問題は、標準では、この既定の引数を使用してパラメーターを連続して省略した結果、同じシグネチャを持つ 2 つのコンストラクターが生成された場合に何が起こるかを実際に指定していないことです。(上記の両方のテンプレート コンストラクターでfoo、既定の引数でパラメーターを省略すると、署名が得られることtemplate<class T> foo(T);に注意してください。) パラグラフ 7 には次のようなメモがあります。

2 つのusing 宣言が同じ署名を持つ継承コンストラクターを宣言する場合、最初の using 宣言によって導入された暗黙的に宣言されたコンストラクターはユーザー宣言されたコンストラクターではないため、プログラムは不適切な形式になります (9.2、13.1)。後続のusing-declarationによる同じ署名を持つコンストラクターの別の宣言。

ここではusing-declarationが 1 つしかないため、注記は当てはまりません。また、宣言の重複は実際に禁止されていますが、段落 1 のセットへの参照は、署名の重複が単純に 1 つとして扱われることを意味することは議論の余地があります。単一のusing 宣言が重複した宣言を導入しないこと。

この問題は、実際には、標準に対する 2 つの欠陥報告の対象となっています: CWG 1645およびCWG 1941であり、これらの欠陥報告がどのように解決されるかは不明です。CWG issue 1645 の 2013 ノートに記載されている 1 つの可能性は、そのような継承されたコンストラクター (複数の基本コンストラクターから派生したもの) を削除して、使用した場合にのみエラーが発生するようにすることです。CWG issue 1941 で提案されている代替アプローチは、継承コンストラクターを、派生クラスに導入された他の基本クラス関数のように動作させることです。

于 2014-09-04T04:02:35.427 に答える