Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't assign a `std::unique_ptr` to a base class in clang when using an alias template

The following code compiles and runs just fine on gcc 4.9.3 and 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>());
}

However, if we change the #if 0 to #if 1, gcc compiles, but clang does not:

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

What's the problem with using the alias template in this context? Or, if gcc is more permissive than it should be, why is that the case?

like image 813
wyer33 Avatar asked Oct 04 '16 01:10

wyer33


1 Answers

This is CWG issue 1244:

The example in 14.4 [temp.type] paragraph 1 reads in significant part,

template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;

and says that y and z have the same type.

This would only be true if alias template Z were considered to be equivalent to class template Y. However, 14.5.7 [temp.alias] describes equivalence only for specializations of alias templates, not for the alias templates themselves. Either such rules should be specified, which could be tricky, or the example should be deleted.

We can reduce your example down to:

std::unique_ptr<Base<double, Buz>> f = 
    std::make_unique<Base<double, Bar>>();

This is well formed if and only if Buz and Bar are considered equivalent. gcc thinks they are, clang thinks they aren't. It's still an open question as to what the actual answer is.

like image 50
Barry Avatar answered Nov 19 '22 02:11

Barry