【问题标题】:Can I ask why the code for vector push_back depending on the data type doesn't work? [duplicate]请问为什么根据数据类型的vector push_back 的代码不起作用? [复制]
【发布时间】:2021-07-09 05:00:33
【问题描述】:

我想根据向量的数据类型用不同的函数填充向量。现在我要考虑三种类型:int、double、string。如果我只考虑 int 和 double,则代码可以完美运行。但是如果我在模板中包含字符串,代码就会失败。我不明白为什么字符串的 if 语句不起作用。代码如下:

#include <iostream>
#include <vector>
#include <map>
#include <string>

template <typename T>
void fillV(std::vector<T> &res) {
    if (std::is_same<T, double>::value ) res.push_back(3.5);
    else if (std::is_same<T, int>::value) res.push_back(2);
    else if (std::is_same<T, std::string>::value) res.push_back(std::string("hello"));
}

int main()
{
    std::vector<std::string> t;
    std::vector<double> t1;
    std::vector<int> t2;
     
    fillV<std::string>(t);
    fillV<double>(t1);
    fillV<int>(t2);
    
    std::cout << t[0] << "\n";
    std::cout << t1[0] << "\n";
    std::cout << t2[0] << "\n";
}

错误信息是

  
input
stderr
Compilation failed due to following error(s).main.cpp: In instantiation of ‘void fillV(std::vector<T>&) [with T = std::basic_string<char>]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',27)">main.cpp:27:25</span>:   required from here
main.cpp:16:42: error: no matching function for call to ‘std::vector >::push_back(double)’
     if (std::is_same<T, double>::value ) res.push_back(3.5);
                                          ^~~
In file included from /usr/include/c++/6/vector:64:0,
                 from main.cpp:10:
/usr/include/c++/6/bits/stl_vector.h:914:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::basic_string; _Alloc = std::allocator >; std::vector<_Tp, _Alloc>::value_type = std::basic_string]
       push_back(const value_type& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:914:7: note:   no known conversion for argument 1 from ‘double’ to ‘const value_type& {aka const std::basic_string&}’
/usr/include/c++/6/bits/stl_vector.h:932:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = std::basic_string; _Alloc = std::allocator >; std::vector<_Tp, _Alloc>::value_type = std::basic_string]
       push_back(value_type&& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:932:7: note:   no known conversion for argument 1 from ‘double’ to ‘std::vector >::value_type&& {aka std::basic_string&&}’
main.cpp:17:43: error: no matching function for call to ‘std::vector >::push_back(int)’
     else if (std::is_same<T, int>::value) res.push_back(2);
                                           ^~~
In file included from /usr/include/c++/6/vector:64:0,
                 from main.cpp:10:
/usr/include/c++/6/bits/stl_vector.h:914:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::basic_string; _Alloc = std::allocator >; std::vector<_Tp, _Alloc>::value_type = std::basic_string]
       push_back(const value_type& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:914:7: note:   no known conversion for argument 1 from ‘int’ to ‘const value_type& {aka const std::basic_string&}’
/usr/include/c++/6/bits/stl_vector.h:932:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = std::basic_string; _Alloc = std::allocator >; std::vector<_Tp, _Alloc>::value_type = std::basic_string]
       push_back(value_type&& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:932:7: note:   no known conversion for argument 1 from ‘int’ to ‘std::vector >::value_type&& {aka std::basic_string&&}’
main.cpp: In instantiation of ‘void fillV(std::vector<T>&) [with T = double]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',28)">main.cpp:28:21</span>:   required from here
main.cpp:18:51: error: no matching function for call to ‘std::vector::push_back(std::string)’
     else if (std::is_same<T, std::string>::value) res.push_back(std::string("hello"));
                                                   ^~~
In file included from /usr/include/c++/6/vector:64:0,
                 from main.cpp:10:
/usr/include/c++/6/bits/stl_vector.h:914:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = double; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::value_type = double]
       push_back(const value_type& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:914:7: note:   no known conversion for argument 1 from ‘std::string {aka std::basic_string}’ to ‘const value_type& {aka const double&}’
/usr/include/c++/6/bits/stl_vector.h:932:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = double; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::value_type = double]
       push_back(value_type&& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:932:7: note:   no known conversion for argument 1 from ‘std::string {aka std::basic_string}’ to ‘std::vector::value_type&& {aka double&&}’
main.cpp: In instantiation of ‘void fillV(std::vector<T>&) [with T = int]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',29)">main.cpp:29:18</span>:   required from here
main.cpp:18:51: error: no matching function for call to ‘std::vector::push_back(std::string)’
     else if (std::is_same<T, std::string>::value) res.push_back(std::string("hello"));
                                                   ^~~
In file included from /usr/include/c++/6/vector:64:0,
                 from main.cpp:10:
/usr/include/c++/6/bits/stl_vector.h:914:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = int; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::value_type = int]
       push_back(const value_type& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:914:7: note:   no known conversion for argument 1 from ‘std::string {aka std::basic_string}’ to ‘const value_type& {aka const int&}’
/usr/include/c++/6/bits/stl_vector.h:932:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = int; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::value_type = int]
       push_back(value_type&& __x)
       ^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:932:7: note:   no known conversion for argument 1 from ‘std::string {aka std::basic_string}’ to ‘std::vector::value_type&& {aka int&&}’

2021 年 4 月 14 日更新: 由于我无法回答自己的问题,因此我将添加除重载之外的另一种解决方案,即通过 Partial 模板专业化。它在 C++17 之前工作。代码灵感来自第一个答案,谢谢。

template <typename T, int i>
struct test;

template <typename T>
struct test<T, 1> {
    void fillV(std::vector<T> &res) {
    res.push_back(3.5);
}
};

template <typename T>
struct test<T, 2> {
    void fillV(std::vector<T> &res) {
    res.push_back(2);
}
};

template <typename T>
struct test<T, 3> {
    void fillV(std::vector<T> &res) {
    res.push_back("Hello");
}
};

int main()
{
    std::vector<std::string> t;
    std::vector<double> t1;
    std::vector<int32_t> t2;
     
    test<double, 1> a1;
    a1.fillV(t1);
    test<int32_t, 2> a2;
    a2.fillV(t2);
    test<std::string, 3> a;
    a.fillV(t);
    
    std::cout << t[0] << "\n";
    std::cout << t1[0] << "\n";
    std::cout << t2[0] << "\n";
}

2021 年 4 月 17 日更新: 而且,如果使用重载,下面的代码就足够了。

void fill_v(std::vector<double>& res) { res.push_back(3.5); }
void fill_v(std::vector<int>& res) { res.push_back(2); }
void fill_v(std::vector<std::string>& res) { res.push_back(std::string("hello")); }

int main()
{
    std::vector<std::string> t;
    std::vector<double> t1;
    std::vector<int32_t> t2;

    fillV(t1);
    fillV(t2);
    fillV(t);

    std::cout << t[0] << "\n";
    std::cout << t1[0] << "\n";
    std::cout << t2[0] << "\n";
}

【问题讨论】:

    标签: c++ templates vector


    【解决方案1】:

    if 在运行时执行调度。在编译时,无论T 是什么,所有if 部分和else 部分都必须是有效的。

    您可以更改为Constexpr If(C++17 起)。

    如果值为真,则丢弃statement-false(如果存在),否则丢弃statement-true。

    如果 constexpr if 语句出现在模板化实体中,并且 if 条件在实例化后不依赖于值,则在实例化封闭模板时不会实例化丢弃的语句。

    例如

    template <typename T>
    void fillV(std::vector<T> &res) {
        if constexpr (std::is_same<T, double>::value ) res.push_back(3.5);
        else if constexpr (std::is_same<T, int>::value) res.push_back(2);
        else if constexpr (std::is_same<T, std::string>::value) res.push_back(std::string("hello"));
    }
    

    LIVE

    在 C++17 之前,您可以添加一些重载来处理 std::vector 的不同变体。例如

    void fill_v(std::vector<double>& res) { res.push_back(3.5); }
    void fill_v(std::vector<int>& res) { res.push_back(2); }
    void fill_v(std::vector<std::string>& res) { res.push_back(std::string("hello")); }
    
    template <typename T>
    void fillV(std::vector<T> &res) {
        if (std::is_same<T, double>::value ) fill_v(res);
        else if (std::is_same<T, int>::value) fill_v(res);
        else if (std::is_same<T, std::string>::value) fill_v(res);
        // or just 
        // fill_v(res);
    }
    

    【讨论】:

    • 非常感谢。这正是我的想法,但找不到确切的答案。在 C++11 之前有没有和 constexpr 一样的东西?因为我只被允许使用 C++11。
    • @ChuanXu C++11 中没有类似的功能;你必须尝试其他方法。我在答案中添加了一个。
    • 知道了。非常感谢。我发现 Partial 模板专业化是另一种解决方案,与您使用的类似。
    猜你喜欢
    • 1970-01-01
    • 2011-08-19
    • 2014-09-26
    • 2013-06-20
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多