Невозможно назначить `std:: unique_ptr` базовому классу в clang при использовании шаблона псевдонима
Следующий код компилируется и работает просто отлично на gcc 4.9.3 и clang 3.7.1
// std::unique_ptr
#include <memory>
// Template class for template-template arguments
template <typename Real>
struct Bar {};
// Base class
template <typename T,template <typename> class XX>
struct Base {};
// Derived class that operates only on Bar
template <typename Real>
struct Derived : public Base <Real,Bar> {};
// Holds the unique_ptr
template <typename T,template <typename> class XX>
struct Foo {
std::unique_ptr <Base <T,XX>> foo;
};
// Create an alias template
template <typename Real>
using Buz = Bar <Real>;
int main() {
#if 0
auto f = Foo <double,Buz> (); //Causes error!
#else
auto f = Foo <double,Bar> ();
#endif
f.foo = std::make_unique <Derived <double>> (Derived <double>());
}
Однако, если мы изменим #if 0
на #if 1
, gcc компилирует, но clang не делает:
g++ -std=c++14 test03.cpp -o test03_gcc
clang++ -std=c++14 test03.cpp -o test03_clang
test03.cpp:32:11: error: no viable overloaded '='
f.foo = std::make_unique <Derived <double>> (Derived <double>());
~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/include/g++-v4/bits/unique_ptr.h:249:7: note:
candidate function not viable: no known conversion from
'unique_ptr<Derived<double>, default_delete<Derived<double>>>' to
'unique_ptr<Base<double, Buz>, default_delete<Base<double, Buz>>>' for
1st argument
operator=(unique_ptr&& __u) noexcept
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/include/g++-v4/bits/unique_ptr.h:278:7: note:
candidate function not viable: no known conversion from 'typename
_MakeUniq<Derived<double> >::__single_object' (aka
'unique_ptr<Derived<double> >') to 'nullptr_t' for 1st argument
operator=(nullptr_t) noexcept
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/include/g++-v4/bits/unique_ptr.h:357:19: note:
candidate function not viable: no known conversion from
'unique_ptr<Derived<double>, default_delete<Derived<double>>>' to
'const unique_ptr<Base<double, Buz>, default_delete<Base<double,
Buz>>>' for 1st argument
unique_ptr& operator=(const unique_ptr&) = delete;
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/include/g++-v4/bits/unique_ptr.h:264:22: note:
candidate template ignored: disabled by 'enable_if' [with _Up =
Derived<double>, _Ep = std::default_delete<Derived<double> >]
typename enable_if< __and_<
^
1 error generated.
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1
В чем проблема с использованием шаблона псевдонима в этом контексте? Или, если gcc более разрешительна, чем это должно быть, почему это так?
Ответы
Ответ 1
Это CWG-выпуск 1244:
Пример в 14.4 [temp.type], пункт 1, читается в значительной части,
template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;
и говорит, что y
и z
имеют один и тот же тип.
Это было бы справедливо только в том случае, если шаблон alias z
считался эквивалентным шаблону класса y
. Тем не менее, 14.5.7 [temp.alias] описывает эквивалентность только для специализаций шаблонов псевдонимов, а не для самих шаблонов псевдонимов. Должны быть указаны либо такие правила, которые могут быть сложными, либо пример должен быть удален.
Мы можем уменьшить ваш пример до:
std::unique_ptr<Base<double, Buz>> f =
std::make_unique<Base<double, Bar>>();
Это хорошо сформировано тогда и только тогда, когда Buz
и Bar
считаются эквивалентными. gcc думает, что они есть, clang думает, что это не так. Все еще остается открытым вопрос, каков фактический ответ.