Ошибка наследования конструктора с boost::multiprecision::mpz_int

Я пытался создать класс, основанный на 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)...)
    {}
};

Кто-нибудь может объяснить, почему первый пример выдает ошибку? Я думал, что наследование конструкторов базового класса и пересылка значений им делали примерно одно и то же. Я предполагаю, что был неправ, но мне все еще интересно знать разницу.

РЕДАКТИРОВАТЬ: Я сделаю все ясно. Мне все равно, есть ли лучшие методы для достижения этого (есть тонны). Единственное, что я спросил, почему наследование конструктора не удалось в этом случае. Это из-за ошибки компилятора или какого-то неясного правила где-то в стандарте?

1 ответ

Решение

По-видимому, это вызвано параметрами по умолчанию mpz_int конструкторы (mpz_int является typedef для конкретного экземпляра boost::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) { }
};

и все же получить тот же результат - ошибка в g++, ОК в Clang.

Теперь вопрос заключается в том, должна ли эта конструкция действительно приниматься в соответствии со стандартом. К сожалению, нет четкого ответа. §12.9 [class.inhctor]/p1 говорит, что

Объявление- использование (7.3.3), которое именует конструктор, неявно объявляет набор наследующих конструкторов. Набор кандидатов унаследованных конструкторов из класса X named в объявлении- использовании состоит из фактических конструкторов и условных конструкторов, которые получаются в результате преобразования параметров по умолчанию следующим образом:

  • все не шаблонные конструкторы X, а также
  • для каждого не шаблонного конструктора X который имеет по крайней мере один параметр с аргументом по умолчанию, набор конструкторов, который получается в результате пропуска любой спецификации параметра с многоточием и последовательного пропуска параметров с аргументом по умолчанию в конце списка параметров-типов, и
  • все шаблоны конструктора X, а также
  • для каждого шаблона конструктора X который имеет по крайней мере один параметр с аргументом по умолчанию, набор шаблонов-конструкторов, который получается в результате пропуска любой спецификации параметра с многоточием и последовательного пропуска параметров с аргументом по умолчанию в конце списка параметров-типов.

Проблема заключается в том, что стандарт на самом деле не определяет, что произойдет, если эта процедура "последовательно пропускающие параметры с аргументами по умолчанию" приводит к двум конструкторам с одинаковой сигнатурой. (Обратите внимание, что с обоими конструкторами шаблона foo выше, пропуск параметра с аргументом по умолчанию дает подпись template<class T> foo(T);.) Хотя в пункте 7 есть примечание, в котором говорится

Если два объявления using объявляют наследуемые конструкторы с одинаковыми сигнатурами, программа плохо сформирована (9.2, 13.1), потому что неявно объявленный конструктор, введенный первым объявлением using, не является объявленным пользователем конструктором и, следовательно, не исключает другое объявление конструктора с такой же подписью последующим объявлением использования.

здесь у нас есть только одно объявление-использование, поэтому примечание не применяется, и, хотя дубликаты объявлений действительно запрещены, можно утверждать, что ссылка на набор в пункте 1 означает, что дубликаты подписей будут просто рассматриваться как одна, поэтому что одно объявление об использовании не будет представлять собой повторное объявление.

Эта проблема фактически является предметом двух отчетов о дефектах по стандарту: CWG 1645 и CWG 1941, и неясно, как будут решаться эти отчеты о дефектах. Одна из возможностей, отмеченная в заметке 2013 года в выпуске 1645 CWG, заключается в удалении таких унаследованных конструкторов (полученных из нескольких базовых конструкторов), чтобы они вызывали ошибку только при использовании. Альтернативный подход, предложенный в выпуске CWG 1941 года, состоит в том, чтобы заставить наследующие конструкторы вести себя подобно другим функциям базового класса, введенным в производный класс.

Другие вопросы по тегам