2015-03-04 9 views
6

Następujący fragment kodu kompiluje się w języku i MSVS, ale nie w gcc.Błąd w gcc lub rozszerzenie w klang/MSVS

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 narzeka:

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)); 

Jest to błąd w gcc, i czy istnieje obejście że tylko opiera się na standardowej zgodnej C++?

+1

Jakiej wersji GCC używasz? Czy próbowałeś już tego, 4.9.2? – usr1234567

+0

g ++ --version daje 'g ++ (GCC) 4.9.2' – sp2danny

+0

Rzeczywiście, wystąpił błąd w GCC 4.9.2 i jest skompilowany z Clang 3.5 –

Odpowiedz

2

Rzeczywiście wydaje się, że to błąd. Rozwiązaniem jest oddzielenie domyślnego parametru szablonu od drugiej funkcji. W ciągu clone_ptr wtedy mieć dwóch przyjaciół:

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); 

i definicja jest prosta:

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...)}; } 

Testowane z gcc 4.8.3 i Clang 3.5.

Edit: Po zbadaniu udało mi się dostać kod pracy z gcc 4.8.3 na dwa różne sposoby:

  1. Zdjąć deklarację funkcji szablon całkowicie

    // this is not needed: 
    template<typename T, typename U, typename ...Args> 
    clone_ptr<T> make_cloned(Args ...args); 
    
  2. Przesuń domyślna definicja parametru szablonu od definicji funkcji szablonu do deklaracji:

    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...)}; 
    } 
    

Nadal zakładam, że jest to problem z gcc, ale w ten sposób twój kod działa.