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

Expanding parameter pack containing initializer_list to constructor

本文关键字:扩展 构造函数 包扩展 参数 initializer 包含 list      更新时间:2023-10-16

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

<>之前函数'int main(int, char**)'中:函数"std::shared_ptr spnew(Args…)[with T = Example, Args ={}]"的参数太多在函数'std::shared_ptr spnew(Args…)[with T = Example, Args ={}]'中:没有匹配的函数来调用'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"});
}

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

你可以这样做-

#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- not -my-template-accept-an-initializer-list,并且可能不是标准兼容,尽管c++0x标准尚未发布,所以这可能会改变。

对于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]

我不知道为什么有人会抱怨初始列表的演绎。

有一个相关的线程:为什么我的模板不接受初始化列表

基本上,一个裸初始化列表没有类型

相关文章: