7

Zamierzam użyć shared_ptr sporo w nadchodzącym projekcie, więc (nie zdając sobie z std::make_shared) Chciałem napisać o zmiennej liczbie argumentów funkcji szablonu spnew<T>(...) jako shared_ptr -returning stand-in dla new . Wszystko poszło gładko, dopóki nie spróbowałem użyć typu, którego konstruktor zawiera initializer_list. Otrzymuję następujący z GCC 4.5.2, gdy próbuję skompilować minimalny przykład poniżej:Rozszerzenie parametru pakiet zawierający initializer_list konstruktorowi

 
In function 'int main(int, char**)': 
too many arguments to function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]' 

In function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]': 
no matching function for call to 'Example::Example()' 

Co dziwne, mam równoważne błędy gdybym zastąpił std::make_shared dla spnew. W obu przypadkach wydaje się, że nieprawidłowo wyprowadza się parametry, gdy w grę wchodzi initializer_list, błędnie traktując Args... jako puste. Oto przykład:

#include <memory> 
#include <string> 
#include <vector> 

struct Example { 

    // This constructor plays nice. 
    Example(const char* t, const char* c) : 
     title(t), contents(1, c) {} 

    // This one does not. 
    Example(const char* t, std::initializer_list<const char*> c) : 
     title(t), contents(c.begin(), c.end()) {} 

    std::string title; 
    std::vector<std::string> contents; 

}; 

// This ought to be trivial. 
template<class T, class... Args> 
std::shared_ptr<T> spnew(Args... args) { 
    return std::shared_ptr<T>(new T(args...)); 
} 

// And here are the test cases, which don't interfere with one another. 
int main(int argc, char** argv) { 
    auto succeeds = spnew<Example>("foo", "bar"); 
    auto fails = spnew<Example>("foo", {"bar"}); 
} 

Czy to tylko niedopatrzenie z mojej strony, czy błąd?

+1

Jest "std :: make_shared", nawiasem mówiąc. – GManNickG

+0

@GMan: Tak, znalazłem to i będę z niego korzystał, ale nadal jestem ciekawy co napisałem. –

+0

@GMan: Właściwie, spróbujmy zastąpić 'make_shared' dla' spnew' w moim przykładzie, nadal nie działa w przypadku 'fail' z odpowiednimi błędami. Teraz przynajmniej wiem, gdzie błąd nie jest ... –

Odpowiedz

0

z gcc-4.7 (prawdopodobnie będzie działać na GCC-4.6 też tylko rozgałęzione) z ostrzeżeniami:

foo.cpp: In function ‘int main(int, char**)’: 
foo.cpp:29:47: warning: deducing ‘Args ...’ as ‘std::initializer_list<const 
char*>’ [enabled by default] 
foo.cpp:22:20: warning: in call to ‘std::shared_ptr<_Tp1> spnew(Args ...) 
[with T = Example, Args = {const char*, std::initializer_list<const 
char*>}]’ [enabled by default] 
foo.cpp:29:47: warning: (you can disable this with -fno-deduce-init-list) 
[enabled by default] 

Nie jestem pewien, dlaczego ktoś chciałby rozbudować o listy startowe odliczenia chociaż.

Jest powiązany wątek: Why doesn't my template accept an initializer list

Zasadniczo, gołe listy startowe nie mają typu.

+1

Jeśli chodzi o wołowinę, domyślnym sposobem odliczenia listy startowej jest starsze rozszerzenie spekulacyjne, które może kolidować z nowszą propozycją, i dlatego pojawiła się '-fno-deduce-init-list'. –

1

Można to zrobić -

#include <memory> 
#include <string> 
#include <iostream> 
#include <vector> 

struct Example { 

    template<class... Args> 
    Example(const char* t, Args... tail) : title(t) 
    { 
     Build(tail...); 
    } 

    template<class T, class... Args> 
    void Build(T head, Args... tail) 
    { 
     contents.push_back(std::string(head)); 
     Build(tail...); 
    } 

    template<class T> 
    void Build(T head) 
    { 
     contents.push_back(std::string(head)); 
    } 

    void Build() {}   

    std::string title; 
    std::vector<std::string> contents; 

}; 

template<class T, class... Args> 
std::shared_ptr<T> spnew(Args... args) { 
    return std::shared_ptr<T>(new T(args...)); 
} 

int main(int argc, char** argv) { 
    auto succeeds = spnew<Example>("foo", "bar"); 
    auto fails = spnew<Example>("foo", "bar", "poo", "doo"); 

    std::cout << "succeeds->contents contains..." << std::endl; 
    for (auto s : succeeds->contents) std::cout << s << std::endl; 

    std::cout << std::endl << "fails->contents contains..." << std::endl; 
    for (auto s : fails->contents) std::cout << s << std::endl; 
} 

Ten, mimo ogólnych szablonów jest typu bezpieczne jak kompilator będzie narzekać się contents.push_back jeśli przekazany typ nie jest zamienny z const char *.

Jak opisano powyżej, kod działa dobrze z gcc 4.6 jednak ostrzeżenie masz wyjaśnione tutaj why-doesnt-my-template-accept-an-initializer-list, i prawdopodobnie nie jest zgodne ze standardami , chociaż C++ 0x standard jest jeszcze opublikowany więc to może zmiana.