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

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

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

13

Решение

Это, кажется, вызвано параметры по умолчанию 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 назван в
используя декларирование состоит из фактических конструкторов и условных конструкторов, которые являются результатом преобразования по умолчанию
параметры следующим образом:

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

9

Другие решения


По вопросам рекламы [email protected]