2015-03-04 2 views
6

Следующий фрагмент компиляции в clang и MSVS, но не в gcc.Ошибка в gcc или расширение в clang/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; 
} 


НКУ жалуется:

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

Является ли это ошибка в НКУ, и есть обходной путь, который только опирается на стандарт-совместимый C++?

+1

Какую версию GCC вы используете? Пробовали ли вы, 4.9.2? – usr1234567

+0

g ++ --version дает 'g ++ (GCC) 4.9.2' – sp2danny

+0

Действительно, он получил ошибку с GCC 4.9.2 и скомпилирован с Clang 3.5 –

ответ

2

Действительно, это похоже на ошибку. Обходным путем является разделение параметра шаблона по умолчанию на вторую функцию. В clone_ptr вы тогда есть два друга:

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

и легко определить:

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

Испытано с GCC 4.8.3 и Clang 3.5.

Edit: После исследования я был в состоянии получить код, работающий с GCC 4.8.3 двумя различными способами:

  1. удалить объявление функции шаблона полностью

    // this is not needed: 
    template<typename T, typename U, typename ...Args> 
    clone_ptr<T> make_cloned(Args ...args); 
    
  2. Перемещение определение параметра шаблона по умолчанию из определения функции шаблона в декларации:

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

Я все еще предполагаю, что это проблема с gcc, но таким образом ваш код работает.

Смежные вопросы