c++ - std::tuple,自动构造顺序

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

考虑以下代码片段

struct MachineGun {
    explicit  MachineGun (int a);
};

struct Turret {
    explicit  Turret (char b);
};

struct Cannon {
    explicit  Cannon (std::string c);
};


template<typename... Ts>
struct Enemy {
    template<typename...Args>
    Enemy(Args&&... args)
        : device_list {std::forward<Args>(args)...}
    {

    }

private:
    std::tuple<Ts...> device_list;
};

在上面的构造函数 std::forward 中,args unpack 匹配元组内类型的正确构造函数,但我无法弄清楚如何尝试进行匹配,而不管类型的顺序如何或参数。

假设下面的代码代表同一件事:“给我一个带机枪、炮塔和大炮的敌人”

Enemy<MachineGun,Turret,Cannon> e1(1,'1',std::string("1"));
Enemy<MachineGun,Turret,Cannon> e2("3",14,'5');
Enemy<Turret,MachineGun,Cannon> e3(std::string("14"), 5, '33');

但实际上我该如何启用它呢? e1 将编译为 args 列表匹配类型列表顺序,e2e3 应该代表相同的东西,但我如何制作我的编译器明白了吗?

最佳答案

我在你的问题中看到的最大问题是相同的值可以初始化你的元组中的两个不同对象的风险。

举例,给定

Enemy<MachineGun,Turret,Cannon> e2("3",14,'5');

整数(14)可以同时初始化MachineGun (一个 int 构造函数)和 Turret (一个 char 构造函数和从 intchar 的隐式转换)。

'5' 同样的问题: 可以同时初始化 MachineGun (一个 int 构造函数和从 charint 的隐式转换)和 Turret (char 构造函数)。

我认为解决此问题的唯一方法是避免构造函数冲突添加已删除的构造函数。

所以你可以添加一个已删除的char MachineGun 的构造函数

struct MachineGun
 {
   explicit MachineGun (int i)
    { /* something */ }

   MachineGun (char) = delete;
 };

和一个删除的int Turret 的构造函数

struct Turret
 {
   explicit Turret (char c) 
    { /* something */ }

   Turret (int) = delete;
 };

更一般地说,您必须添加所有必要的已删除构造函数以避免所有可能的歧义,并为传递给 Enemy 的每个参数只启用一个构造函数。构造函数。

无论如何,考虑到这种简化,您可以编写几个 SFINAE 替代函数来从列表中选择正确的元素来构造对象

template <typename T, typename A0, typename ... As>
std::enable_if_t<true == std::is_constructible<T, A0>::value, T>
    selectArg (A0 && a0, As ...)
 { return T{ std::forward<A0>(a0) }; }

template <typename T, typename A0, typename ... As>
std::enable_if_t<false == std::is_constructible<T, A0>::value, T>
    selectArg (A0, As && ... as)
 { return selectArg<T>(std::forward<As>(as)...); }

Enemy简单地成为

template <typename ... Ts>
struct Enemy
 {
   private:
      std::tuple<Ts...> device_list;

   public:
      template <typename ... Args>
      Enemy (Args ... args)
        : device_list { selectArg<Ts>(args...)... }
       { }
 };

下面是一个完整的工作示例

#include <tuple>
#include <iostream>
#include <type_traits>

struct MachineGun
 {
   explicit MachineGun (int i)
    { std::cout << "- MachineGun: " << i << std::endl; }

   MachineGun (char) = delete;
 };

struct Turret
 {
   explicit Turret (char c) 
    { std::cout << "- Turret: " << c << std::endl; }

   Turret (int) = delete;
 };

struct Cannon
 {
   explicit Cannon (std::string const & s)
    { std::cout << "- Cannon: " << s << std::endl; }
 };

template <typename T, typename A0, typename ... As>
std::enable_if_t<true == std::is_constructible<T, A0>::value, T>
    selectArg (A0 && a0, As ...)
 { return T{ std::forward<A0>(a0) }; }

template <typename T, typename A0, typename ... As>
std::enable_if_t<false == std::is_constructible<T, A0>::value, T>
    selectArg (A0, As && ... as)
 { return selectArg<T>(std::forward<As>(as)...); }

template <typename ... Ts>
struct Enemy
 {
   private:
      std::tuple<Ts...> device_list;

   public:
      template <typename ... Args>
      Enemy (Args ... args)
        : device_list { selectArg<Ts>(args...)... }
       { }
 };

int main()
 {
   Enemy<MachineGun,Turret,Cannon> e1(1,'2',std::string("3"));
   Enemy<MachineGun,Turret,Cannon> e2("3",14,'5');
   Enemy<Turret,MachineGun,Cannon> e3(std::string("14"), 5, '3');
 }

使用 typename std::enable_if<...>::type而不是 std::enable_if_t<...>该解决方案也适用于 C++11。

关于c++ - std::tuple,自动构造顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49412609/

相关文章:

html - 如何创建 DRY HTML?

c++ - 无法将字符串传递给我的类函数

c++ - 通过命令行中的ffmpeg或opencv从IP摄像头捕获图像获取灰度图像

c++ - 使用 Module32First/Next 从 64 位进程枚举 32 位进程模块

c++ - 将 std::tuple 的类型转换为 std::pair

c++ - c++11 lambda 的代码可读性

multithreading - c++11线程的优势

c++ - 只有一个模板特化有用吗?

c++ - 与 using 声明冲突的重载

c++ - 这个模板参数推导是如何工作的?