Подтвердить что ты не робот

Ошибка наследования конструктора с 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)...)
    {}
};

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

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

4b9b3361

Ответ 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 компилирует воспроизводимый код выше, он фактически не работает, если вы попытаетесь использовать наследуемый конструктор с одним аргументом, который почти одинаково как плохо. Вы можете заставить его работать в clang, однако, явно поставляя второй параметр.)

Мы можем даже пропустить шаблонность для конструкторов foo, просто используя вместо этого:

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

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

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

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

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

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

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

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

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