Я пытался создать класс, основанный на 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)...)
{}
};
Кто-нибудь может объяснить, почему первый пример выдает ошибку? Я думал, что наследование конструкторов базового класса и пересылка значений им делали примерно одно и то же. Я предполагаю, что был неправ, но мне все еще интересно знать разницу.
РЕДАКТИРОВАТЬ: Я все проясню. Мне все равно совсем есть ли лучшие методы для достижения этого (есть тонны). Единственное, что я спросил, почему наследование конструктора не удалось в этом случае. Это связано с ошибкой компилятора или каким-то неясным правилом где-то в стандарте?
Это, кажется, вызвано параметры по умолчанию 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 года, состоит в том, чтобы заставить наследуемые конструкторы вести себя подобно другим функциям базового класса, введенным в производный класс.