首页 文章

将包含initializer_list的参数包扩展为构造函数

提问于
浏览
7

我打算在即将到来的项目中使用 shared_ptr ,所以(不知道 std::make_shared )我想写一个可变参数模板函数 spnew<T>(...) 作为 shared_ptrshared_ptr -returning替身 . 一切顺利,直到我试图使用其构造函数包含 initializer_list 的类型 . 当我尝试编译下面的最小示例时,我从GCC 4.5.2得到以下内容:

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()'

奇怪的是,如果我用 std::make_shared 代替 spnew ,我会得到相同的错误 . 在任何一种情况下,当涉及 initializer_list 时,似乎错误地推断出参数,错误地将 Args... 视为空 . 这是一个例子:

#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"});
}

这只是我的疏忽,还是一个错误?

2 回答

  • 1

    你可以这样做 -

    #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;
    }
    

    这,尽管通用模板是类型安全的,因为如果传递的类型不能转换为 const char * ,编译器会抱怨 contents.push_back .

    如上所述,您的代码在gcc 4.6中正常工作,但是您在此处解释了警告why-doesnt-my-template-accept-an-initializer-list,并且可能不符合标准,尽管c 0x标准尚未发布,因此可能会发生变化 .

  • 0

    使用gcc-4.7(可能也适用于gcc-4.6,只是分支)并带有警告:

    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]
    

    我不知道为什么有人会想要加入init-list演绎 .

    有一个相关的主题:Why doesn't my template accept an initializer list

    基本上,裸init-list没有类型 .

相关问题