别名模板或专门的派生类?

时间:2017-12-09 07:44:16

标签: c++ templates c++17

template<typename T, int V>
class AwesomeClass    // [1]
{
public:

    AwesomeClass(T baseValue)
        : m_value(baseValue + V)
    {
    }

    T get() const
    {
        return m_value;
    }

private:

    T m_value;
};

#ifdef USE_ALIAS_TEMPLATES    // [2]

template<typename T>
using AwesomeClassWithOne = AwesomeClass<T, 1>;

template<typename T>
using AwesomeClassWithTwo = AwesomeClass<T, 2>;

#else    // [4]

template<typename T>
class AwesomeClassWithOne: public AwesomeClass<T, 1>
{
    using AwesomeClass<T, 1>::AwesomeClass;
};

template<typename T>
class AwesomeClassWithTwo: public AwesomeClass<T, 2>
{
    using AwesomeClass<T, 2>::AwesomeClass;
};

AwesomeClassWithOne(int) -> AwesomeClassWithOne<int>;
AwesomeClassWithTwo(int) -> AwesomeClassWithTwo<int>;

#endif

int main()
{
    AwesomeClassWithOne firstObj(20);    // [3]
    AwesomeClassWithTwo secondObj(20);

    return firstObj.get() + secondObj.get();
}

我有一个班级AwesomeClass [1],它有两个模板参数,typename Tint VV是我不希望向该类用户公开的内部详细信息,因此我想提供仅AwesomeClassWithOneAwesomeClassWithTwo之类的别名,例如TV }作为模板参数,并且已将 const {campaignOptions, campaignValue, campaignName} = this.props; ... <Typeahead labelKey="label" allowNew placeholder="Campaign Name..." onInputChange={campaignName} options={campaignOptions} selected={campaignValue} /> 绑定到某个值(在我的示例中分别为1和2)。

Alias模板似乎适合这个[2]。但是,从C ++ 17开始,似乎参数推导不能与别名模板一起使用,所以我做不到[3]。

所以我想出了一个替代[4],它涉及为我想要的每个“别名”类型创建新的派生类,继承基类中的所有构造函数,然后创建演绎指南以使自动演绎工作(基类构造函数)似乎没有像普通构造函数那样导致自动演绎指南; related question)。

这看起来像是一个合适的解决方法吗?与别名模板解决方案相比,我可以期待看到任何奇怪的副作用吗?

2 个答案:

答案 0 :(得分:1)

遗憾的是,C ++ 17不接受类型别名的演绎指南。

但你可以使用古老的make-something方式(std::make_pair()std::make_tuple()std::make_unique()等。)

例如:如果您定义,切换模板参数的顺序,makeAwesome()函数如下

template <int V, typename T>
AwesomeClass<T, V> makeAwesome (T && t)
 { return { std::forward<T>(t) }; 

您可以获取AwesomeClass<T, V>个对象,说明V并推断T,将其调用如下

auto firstObj = makeAwesome<1>(20);

如果您不希望向用户公开整数V部分,则可以添加

template <typename T>
auto makeAwesomeWithOne (T && t)
 { return makeAwesome<1>( std::forward<T>(t) ); }

template <typename T>
auto makeAwesomeWithTwo (T && t)
 { return makeAwesome<2>( std::forward<T>(t) ); }

并且在main()中你可以写

auto firstObj  = makeAwesomeWithOne(20);
auto secondObj = makeAwesomeWithTwo(20);

我知道编写大量几乎等于的makeAwesomeWith-number函数很烦人。

如果使用旧的C风格的函数宏不会吓到你(它们是邪恶的,恕我直言,但在这样的情况......)你可以自动神奇地创建makeAwesomeWith-number函数,如下所示

#define MakeAwesomeWith(Str, Num) \
   template <typename T> \
   auto makeAwesomeWith ## Str (T && t) \
    { return makeAwesome<Num>( std::forward<T>(t) ); }

MakeAwesomeWith(One, 1);
MakeAwesomeWith(Two, 2);
// ...

以下是完整的工作(基于宏)示例

#include <iostream>
#include <utility>

template <typename T, int V>
class AwesomeClass
 {
   public:
      AwesomeClass (T baseValue) : m_value(baseValue + V)
       { }

      T get () const
       { return m_value; }

   private:
      T m_value;
 };

template <int V, typename T>
AwesomeClass<T, V> makeAwesome (T && t)
 { return { std::forward<T>(t) }; }

#define MakeAwesomeWith(Str, Num) \
   template <typename T> \
   auto makeAwesomeWith ## Str (T && t) \
    { return makeAwesome<Num>( std::forward<T>(t) ); }

MakeAwesomeWith(One, 1);
MakeAwesomeWith(Two, 2);

int main ()
 {
   auto firstObj  = makeAwesomeWithOne(20);
   auto secondObj = makeAwesomeWithTwo(20);

   std::cout << (firstObj.get() + secondObj.get()) << std::endl;
 }

答案 1 :(得分:1)

使用别名模板。

类模板参数推导不适用于别名模板(并且不清楚这可能意味着什么),但是不清楚类模板参数推导是否适用于inherited constructors either。这仍然是一个悬而未决的问题,需要加以解决。

此外,您实际上并不想要引入新类型 。您非常希望AwesomeClass<T, 1>成为类型。因此,AwesomeClassWithOne<T>AwesomeClass<T, 1>实际上不同会导致问题。这些不是你真正想要的问题,特别是如果你这样做的唯一理由是允许类模板参数推断。

如果真的需要该功能,请提供make_*()帮助器,就像我们以前习惯写的一样。

相关问题