Error in gcc or extension in clang / MSVS

The following compilation snippet is in clang and MSVS, but not in gcc.

template<typename T> class clone_ptr; template<typename T, typename U, typename ...Args> clone_ptr<T> make_cloned( Args ...args ); // note: everything not needed for example cut out, so // this class is neither complete nor correct template<typename T> class clone_ptr { public: clone_ptr() : ptr(nullptr) {} operator bool() { return ptr!=nullptr; } T* operator->() { return ptr; } private: clone_ptr(T* p) : ptr(p) {} T* ptr; template<class T1,class U1, typename ...Args> friend clone_ptr<T1> make_cloned( Args ...args ); }; template<typename T, typename U=T, typename ...Args> clone_ptr<T> make_cloned( Args ...args ) { return {new U(args...)}; } // ---------------------------------------------- #include <string> #include <vector> #include <iostream> using namespace std; struct Base { int a; Base( int a=0 ) : a(a) {} virtual string foo() { return "Base "+to_string(a); }; virtual ~Base() {} }; struct Sub : Base { Sub( int a=0 ) : Base(a) {} virtual string foo() override { return "Sub "+to_string(a); }; }; string testit() { std::vector< clone_ptr< Base > > vec; vec.push_back( make_cloned<Base>(7) ); vec.emplace_back(); vec.push_back( make_cloned<Base,Sub>(5) ); string ss; for( auto&& a : vec ) { ss += a?a->foo():"<empty>"; } return ss; } int main() { cout << testit() << endl; } 


gcc complains:

 error: no matching function for call to 'make_cloned(int)' vec.push_back( make_cloned<Base>(7) ); note: candidate is: note: template<class T, class U, class ... Args> clone_ptr<T> make_cloned(Args ...) clone_ptr<T> make_cloned( Args ...args ) ^ note: template argument deduction/substitution failed: note: couldn't deduce template parameter 'U' vec.push_back( make_cloned<Base>(7) ); 

Is this a bug in gcc, and is there a workaround that only relies on standard compatible C ++?

+6
source share
1 answer

Indeed, this seems like a mistake. A workaround is to split the default template parameter into a second function. Inside clone_ptr you have two friends:

 template<class T1, typename ...Args> friend clone_ptr<T1> make_cloned( Args ...args ); template<class T1, class U1, typename ...Args> friend clone_ptr<T1> make_cloned( Args ...args ); 

and the definition is easy:

 template<typename T, typename ...Args> clone_ptr<T> make_cloned( Args ...args ) { return {new T(args...)}; } template<typename T, typename U, typename ...Args> clone_ptr<T> make_cloned( Args ...args ) { return {new U(args...)}; } 

Tested with gcc 4.8.3 and clang 3.5.

Edit: After learning, I was able to get your code working with gcc 4.8.3 in two different ways:

  • Delete the declaration of the template function completely

     // this is not needed: template<typename T, typename U, typename ...Args> clone_ptr<T> make_cloned( Args ...args ); 
  • Move the default template parameter definition from the template function definition to the declaration:

     template<typename T, typename U = T, typename ...Args> clone_ptr<T> make_cloned( Args ...args ); template<typename T, typename U, typename ...Args> clone_ptr<T> make_cloned( Args ...args ) { return {new U(args...)}; } 

I still assume this is a problem with gcc, but this way your code works.

+2
source

Source: https://habr.com/ru/post/983314/


All Articles