【问题标题】:implicit std::pair construction in variadic template construction可变参数模板构造中的隐式 std::pair 构造
【发布时间】:2015-09-19 15:14:25
【问题描述】:

我有一个constexpr 键值映射,大致有这个定义:

// map with `pos` remaining entries
template<typename K, typename V, size_t pos>
class Map {
public:
    template<class Head, class... Tail>
    constexpr Map(Head head, Tail... tail)
        :
        value{head},
        tail{tail...} {}

    Element<K, V> value;
    const Map<K, V, pos - 1> tail;

    // members etc
};

// map end element.
template<typename K, typename V>
class Map<K, V, 0> {
public:
    constexpr Map() {}

    // end element specifics.
};

为了在编译时初始化一个键值映射,我有一个实用函数来转发元素:

template<typename K, typename V, typename... Entries>
constexpr Map<K, V, sizeof...(Entries)> create_const_map(Entries&&... entry) {
    return Map<K, V, sizeof...(entry)>(Entry<K, V>{std::forward<Entries>(entry)}...);
}

Element 定义为:

template<class K, class V>
class Entry {
public:
    constexpr Entry(const K &key, const V &value)
        :
        key{key},
        value{value} {}

    constexpr Entry(std::pair<K, V> pair)
        :
        key{pair.first},
        value{pair.second} {}

    const K key;
    const V value;
};

要实际创建地图,我可以成功使用std::make_pair

constexpr auto cmap = create_const_map<int, int>(
    std::make_pair(0, 0),
    std::make_pair(13, 37),
    std::make_pair(42, 9001)
);

我现在想要的是消除对std::make_pair 的调用并改用大括号:

constexpr auto cmap = create_const_map<int, int>(
    {0, 0},
    {13, 37},
    {42, 9001}
);

但这会导致编译失败,因为 {} 没有被推断为对的构造:

/home/jj/devel/openage/libopenage/datastructure/tests.cpp:191:24: error: no matching function for call to 'create_const_map'
        constexpr auto cmap = create_const_map<int, int>(
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~
/home/jj/devel/openage/libopenage/datastructure/constexpr_map.h:286:46: note: candidate function not viable: requires 0 arguments, but 3 were provided
constexpr Map<K, V, sizeof...(Entries)> create_const_map(Entries&&... entry) {
                                        ^

我该怎么做才能使用{a, b} 代替std::make_pair(a, b)

【问题讨论】:

  • 代替Entries的参数包,取一个新模板参数Einitializer_list
  • create_mapcreate_const_map是同一个功能吗?
  • @DanielJour 是的,我纠正了错字。
  • 在不知道 Map 是什么的情况下无法进行实验以更好地理解。我知道 std::map 是什么,但不知道 Map。
  • @mattnewport 不幸的是,他必须将大小作为模板参数传递,但对于 initializer_list,大小不被称为可作为模板参数传递的常量表达式。结果是,也许是为了消除这个尺寸参数。它是用来做什么的?

标签: c++ templates c++11 c++14 variadic-templates


【解决方案1】:

这并不容易,因为正如@Rostislav 所说,如果参数是大括号初始化列表,编译器无法为其推断类型。

我通过手动创建足够多的重载函数来解决这个问题。在下文中,重载是函数调用运算符 (operator()),create_map 是该函数对象的变量模板。您将要创建的重载数量传递给Overload。我的 C++ 很粗糙,所以也许这并不理想,但它似乎可以工作。

template<typename D, int N, typename ...E>
struct OverloadImpl;

template<typename D, int N, typename E, typename ...Es>
struct OverloadImpl<D, N, E, Es...> : OverloadImpl<D, N-1, E, E, Es...> {
    decltype(auto) operator()(const E& e, const Es&... es) {
       return static_cast<D&>(*this).exec(e, es...);
    }
    using OverloadImpl<D, N-1, E, E, Es...>::operator();
};

template<typename D, typename E, typename ...Es>
struct OverloadImpl<D, 0, E, Es...> { 
    decltype(auto) operator()() { 
       return static_cast<D&>(*this).exec();   
    }    
};

template<typename D, typename E, int N>
struct Overload : OverloadImpl<D, N, E>
{ };

要将上述内容应用于您的案例,您可以从Overload 派生并提供exec 函数。

template<typename K, typename V, int C>
struct M {
   template<typename ...T>
   M(T&&...) { }
};

template<typename K, typename V>
struct Entry {
   Entry(K, V) { }    
};

template<typename K, typename V>
struct CreateMapImpl : Overload<CreateMapImpl<K, V>, Entry<K, V>, 10> {
    template<typename ...T>
    M<K, V, sizeof...(T)> exec(const T&... t) {
       return { t... };   
    }
};

template<typename K, typename V>
CreateMapImpl<K, V> create_map{};

int main() {
   create_map<int, std::string>({1, "one"}, {2, "two"}, {3, "three"});   
}

【讨论】:

  • 我的 C++ 很粗糙”对我来说看起来很棒——你在说什么?
  • 好吧,我想 C++ 中很少有“不可能”的事情。不错的一个:)
【解决方案2】:

在我看来,做你需要的事情是不可能的。编译器必须推断Entries 参数包的类型。但是,您要传入的大括号初始值设定项没有类型。来自cppreference,备注部分:

braced-init-list 不是表达式,因此没有类型,例如decltype({1,2}) 格式不正确。没有类型意味着模板类型推导不能推导出与花括号初始化列表匹配的类型,因此给定声明 template&lt;class T&gt; void f(T); 表达式 f({1,2,3}) 是错误的。

这就是为什么 gcc(根据 cpp.sh)说 Entries = {}

63:1:错误:函数 'constexpr Map create_const_map(Entries&& ...) 的参数太多 [with K = int; V = 整数;条目 = {}]'

我想如果你想要一种更简洁的方式来初始化你的地图,你需要为std::make_pair 做一个简写符号。

【讨论】:

    猜你喜欢
    • 2019-05-30
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 2016-09-02
    • 2018-05-18
    • 2016-01-02
    • 1970-01-01
    相关资源
    最近更新 更多