C++:使用类型作为映射键引入模板替换错误

C++: Using type as map key introduces template substitution errors

我使用元组作为键来跟踪地图中的元素,然后在地图上迭代以生成地图的字符串版本。为了帮助我进行转换,我有一个模板便利函数,它将连接用作键的元组(受 this answer 启发)。

#include <iostream>
#include <string>
#include <sstream>
#include <tuple>

template<std::size_t idx = 0, typename ...T>
inline typename std::enable_if<idx == sizeof...(T), void>::type
cat_tuple(std::tuple<T...> &t, std::stringstream &s){
}

template<std::size_t idx = 0, typename ...T>
inline typename std::enable_if<idx < sizeof...(T), void>::type
cat_tuple(std::tuple<T...> &t, std::stringstream &s){
    if (idx != 0)
        s << ",";
    s << std::get<idx>(t);
    cat_tuple<idx+1, T...>(t, s);
}

typedef std::tuple<int, int> my_tuple;
int main(){
    my_tuple t(1, 2);
    std::stringstream s;
    cat_tuple(t, s);
    std::cout << s.str() << std::endl; //Correctly prints "1,2"
}

我可以将元素插入地图并进行迭代而不会出错

#include <iostream>
#include <string>
#include <sstream>
#include <tuple>
#include <map>

typedef std::tuple<int, int> my_tuple;
typedef std::map<my_tuple, int> my_map;
int main(){
    my_map m;
    my_tuple t(1, 2);
    m.insert(std::pair<my_tuple, int>(t, 1));
    std::stringstream s;
    for(my_map::iterator i = m.begin(); i != m.end(); ++i)
        s << i->second;
    std::cout << s.str() << std::endl; //Correctly prints "1"
}

但是当我尝试遍历地图时,出现替换错误:

#include <iostream>
#include <string>
#include <sstream>
#include <tuple>
#include <map>

template<std::size_t idx = 0, typename ...T>
inline typename std::enable_if<idx == sizeof...(T), void>::type
cat_tuple(std::tuple<T...> &t, std::stringstream &s){
}

template<std::size_t idx = 0, typename ...T>
inline typename std::enable_if<idx < sizeof...(T), void>::type
cat_tuple(std::tuple<T...> &t, std::stringstream &s){
    if (idx != 0)
        s << ",";
    s << std::get<idx>(t);
    cat_tuple<idx+1, T...>(t, s);
}

typedef std::tuple<int, int> my_tuple;
typedef std::map<my_tuple, int> my_map;
int main(){
    my_map m;
    my_tuple t(1, 2);
    m.insert(std::pair<my_tuple, int>(t, 1));
    std::stringstream s;
    for(my_map::iterator i = m.begin(); i != m.end(); ++i){
        if (i != m.begin())
            s << "\n";
        cat_tuple(i->first, s); //Substitution error, see below
        s << " " << i->second;
    }
    std::cout << s.str() << std::endl;
}

生产(OSX 上的 g++ 4.2.1,删除了无关的 enable_if 注释)

$ g++ asdf.cc -std=c++11
asdf.cc:31:3: error: no matching function for call to 'cat_tuple'
                cat_tuple(i->first, s); //Substitution error, see below
                ^~~~~~~~~
...
asdf.cc:14:1: note: candidate template ignored: substitution failure [with idx =
      0, T = <int, int>]
cat_tuple(std::tuple<T...> &t, std::stringstream &s){
^
1 error generated.

正如错误消息中所说,由于替换失败,我希望它使用的模板被忽略了。我把它移到地图上时引入了什么不同,我将如何纠正它?

将您的 cat_tuple 函数模板更改为

cat_tuple(std::tuple<T...> const& t, std::stringstream &s)
//                         ^^^^^

这是必要的,因为 std::map 的键是 const

Live demo

无论如何,cat_tupletuple 参数应该是 const&,因为它不会修改参数。


您可能希望将 cat_tuple 重命名为 tuple_printer 或类似名称,并将 std::stringstream 参数更改为 std::ostream,这样您就可以将其传递给 std::cout 如果需要输出到 stdout.

另外,现在的名字太让人联想到std::tuple_cat