根据模板参数有条件编译时包含/排除代码?

发布于 2024-11-01 14:49:08 字数 1291 浏览 5 评论 0原文

考虑以下类,其中内部结构 Y 用作类型,例如。在模板中,稍后:

template<int I>
class X{
  template<class T1>
  struct Y{};

  template<class T1, class T2>
  struct Y{};
};

现在,这个示例显然无法编译,并出现第二个 X::Y 已经定义或者模板参数太多的错误。
我想在没有(额外)部分专业化的情况下解决这个问题,因为 int I 参数不是唯一的参数,并且它的位置在不同的部分专业化中可能有所不同(我的实际结构看起来更像这样,上面只是为了简单起见),所以我希望一个类适合每个我的解决方案。


我的第一个想法显然是 enable_if,但这对我来说似乎失败了,例如。我仍然遇到同样的错误:

// assuming C++11 support, else use boost
#include <type_traits>

template<int I>
class X{
  template<class T1, class = std::enable_if<I==1>::type>
  struct Y{};

  template<class T1, class T2, class = std::enable_if<I==2>::type>
  struct Y{};
};

因此,由于 enable_if 失败,我希望有另一种方法来实现以下编译时检查:

template<int I>
class X{
  __include_if(I == 1){
    template<class T1>
    struct Y{};
  }

  __include_if(I == 2){
    template<class T1, class T2>
    struct Y{};
  }
};

这只是为了节省我很多代码重复,但如果有可能的话我会非常高兴。
编辑:遗憾的是,我无法使用明显的:可变参数模板,因为我使用的是 Visual Studio 2010,因此我只能使用那里支持的 C++0x 内容。 :/

Consider the following class, with the inner struct Y being used as a type, eg. in templates, later on:

template<int I>
class X{
  template<class T1>
  struct Y{};

  template<class T1, class T2>
  struct Y{};
};

Now, this example will obviously not compile, with the error that the second X<I>::Y has already been defined or that it has too many template parameters.
I'd like to resolve that without (extra) partial specialization, since the int I parameter isn't the only one and the position of it can differ in different partial specializations (my actual struct looks more like this, the above is just for simplicity of the question), so I'd like one class fits every I solution.


My first thought was obviously enable_if, but that seems to fail on me, eg. I still get the same errors:

// assuming C++11 support, else use boost
#include <type_traits>

template<int I>
class X{
  template<class T1, class = std::enable_if<I==1>::type>
  struct Y{};

  template<class T1, class T2, class = std::enable_if<I==2>::type>
  struct Y{};
};

So, since enable_if fails, I hope there is another way to achieve the following compile time check:

template<int I>
class X{
  __include_if(I == 1){
    template<class T1>
    struct Y{};
  }

  __include_if(I == 2){
    template<class T1, class T2>
    struct Y{};
  }
};

It would just be to save me a lot of code duplication, but I'd be really happy if it was somehow possible.
Edit: Sadly, I can't use the obvious: variadic templates, as I'm using Visual Studio 2010, so only the C++0x stuff that is supported there I can use. :/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

峩卟喜欢 2024-11-08 14:49:08

这里有两个问题:

  1. enable_if 适用于部分专业化,而不适用于主模板。
  2. 外部可见参数的数量由主模板决定,其中可能只有一个。

回答 1.

正如您在聊天中所建议的,模板的链接列表可以模拟可变参数包。

template<int I>
class X{
  template<class list, class = void>
  struct Y;

  template<class list>
  struct Y< list, typename std::enable_if<I==1>::type > {
      typedef typename list::type t1;
  };

  template<class list>
  struct Y< list, typename std::enable_if<I==2>::type > {
      typedef typename list::type t1;
      typedef typename list::next::type t2;
  };
};

如果您最终得到 next::next::next 垃圾,那么编写元函数或使用 Boost MPL 很容易。


答案 2:

不同数量的模板可以类似地命名,但如果它们嵌套在 SFINAE 控制的类型中,它们仍然保持不同。

template<int I>
class X{
  template<typename = void, typename = void>
  struct Z;

  template<typename v>
  struct Z< v, typename std::enable_if<I==1>::type > {
      template<class T1>
      struct Y{};
  };

  template<typename v>
  struct Z< v, typename std::enable_if<I==2>::type > {
      template<class T1, class T2>
      struct Y{};
  };
};

X<1>::Z<>::Y< int > a;
X<2>::Z<>::Y< char, double > b;

There are two problems here:

  1. enable_if works with partial specialization, not primary templates.
  2. The number of externally-visible arguments is determined by the primary template, of which there may be only one.

Answer 1.

As you suggested in chat, a linked list of templates can emulate the variadic parameter pack.

template<int I>
class X{
  template<class list, class = void>
  struct Y;

  template<class list>
  struct Y< list, typename std::enable_if<I==1>::type > {
      typedef typename list::type t1;
  };

  template<class list>
  struct Y< list, typename std::enable_if<I==2>::type > {
      typedef typename list::type t1;
      typedef typename list::next::type t2;
  };
};

If you end up with next::next::next garbage, it's easy to write a metafunction, or use Boost MPL.


Answer 2.

The different-arity templates can be named similarly but still stay distinct if they are nested inside the SFINAE-controlled type.

template<int I>
class X{
  template<typename = void, typename = void>
  struct Z;

  template<typename v>
  struct Z< v, typename std::enable_if<I==1>::type > {
      template<class T1>
      struct Y{};
  };

  template<typename v>
  struct Z< v, typename std::enable_if<I==2>::type > {
      template<class T1, class T2>
      struct Y{};
  };
};

X<1>::Z<>::Y< int > a;
X<2>::Z<>::Y< char, double > b;
涙—继续流 2024-11-08 14:49:08

给你:

http://ideone.com/AdEfl

代码:

#include <iostream>

template <int I>
struct Traits
{
  struct inner{};
};

template <>
struct Traits<1>
{
  struct inner{
    template<class T1>
    struct impl{
      impl() { std::cout << "impl<T1>" << std::endl; }
    };
  };
};

template <>
struct Traits<2>
{
  struct inner{
    template<class T1, class T2>
    struct impl{
      impl() { std::cout << "impl<T1, T2>" << std::endl; }
    };
  };
};

template<class T>
struct Test{};

template<class T, class K>
struct Foo{};

template<int I>
struct arg{};

template<
  template<class, class> class T,
  class P1, int I
>
struct Test< T<P1, arg<I> > >{
  typedef typename Traits<I>::inner inner;      
};

template<
  template<class, class> class T,
  class P2, int I
>
struct Test< T<arg<I>, P2 > >{
  typedef typename Traits<I>::inner inner;      
};

// and a bunch of other partial specializations

int main(){

  typename Test<Foo<int, arg<1> > >::inner::impl<int> b;
  typename Test<Foo<int, arg<2> > >::inner::impl<int, double> c;
}

说明:基本上它是部分特化的想法,但不同之处在于,不是在 Test 内专门化,而是委托给可以单独专门化于 I 的特定类。这样,您只需为每个 I 定义 inner 版本一次。然后 Test 的多个特化可以重用。 inner 持有者用于使 Test 类中的 typedef 更易于处理。

编辑:这是一个测试用例,显示如果传入错误数量的模板参数会发生什么: http://ideone.com /QzgNP

Here you go:

http://ideone.com/AdEfl

And the code:

#include <iostream>

template <int I>
struct Traits
{
  struct inner{};
};

template <>
struct Traits<1>
{
  struct inner{
    template<class T1>
    struct impl{
      impl() { std::cout << "impl<T1>" << std::endl; }
    };
  };
};

template <>
struct Traits<2>
{
  struct inner{
    template<class T1, class T2>
    struct impl{
      impl() { std::cout << "impl<T1, T2>" << std::endl; }
    };
  };
};

template<class T>
struct Test{};

template<class T, class K>
struct Foo{};

template<int I>
struct arg{};

template<
  template<class, class> class T,
  class P1, int I
>
struct Test< T<P1, arg<I> > >{
  typedef typename Traits<I>::inner inner;      
};

template<
  template<class, class> class T,
  class P2, int I
>
struct Test< T<arg<I>, P2 > >{
  typedef typename Traits<I>::inner inner;      
};

// and a bunch of other partial specializations

int main(){

  typename Test<Foo<int, arg<1> > >::inner::impl<int> b;
  typename Test<Foo<int, arg<2> > >::inner::impl<int, double> c;
}

Explanation: Basically it's an extension of the idea of partial specialization, however the difference is that rather than specializing within Test, delegate to a specific class that can be specialized on I alone. That way you only need to define versions of inner for each I once. Then multiple specializations of Test can re-use. The inner holder is used to make the typedef in the Test class easier to handle.

EDIT: here is a test case that shows what happens if you pass in the wrong number of template arguments: http://ideone.com/QzgNP

只是偏爱你 2024-11-08 14:49:08

你可以尝试下面(这不是部分专业化):

template<int I>
class X
{
};

template<>
class X<1>
{
  template<class T1>
  struct Y{};
};

template<>
class X<2>
{
  template<class T1, class T2>
  struct Y{};
};

我怀疑答案是否那么简单!

编辑(模拟部分专业化)
@Xeo,我能够编译以下代码并且似乎很充实。

template<int I>
struct X
{
  struct Unused {};  // this mocking structure will never be used

  template<class T1, class T2 = Unused>  // if 2 params passed-->ok; else default='Unused'
  struct Y{};

  template<class T1> 
  struct Y<T1, Unused>{}; // This is specialization of above, define it your way
};

int main()
{
  X<1>::Y<int> o1;  // Y<T1 = int, T2 = Unused> called
  X<2>::Y<int, float> o2; // Y<T1 = int, T2 = float> called
}

然而,在这里您可以使用 X<1>、X<2> 等。可以互换。但在你提到的更广泛的例子中,这是无关紧要的。不过,如果需要,您可以检查 I = 1I = 2

Can you try below (it is not partial specialization):

template<int I>
class X
{
};

template<>
class X<1>
{
  template<class T1>
  struct Y{};
};

template<>
class X<2>
{
  template<class T1, class T2>
  struct Y{};
};

I doubt if the answer is that simple !!

Edit (Mocking Partial specialization):
@Xeo, I was able to compile following code and seems to be fullfilling.

template<int I>
struct X
{
  struct Unused {};  // this mocking structure will never be used

  template<class T1, class T2 = Unused>  // if 2 params passed-->ok; else default='Unused'
  struct Y{};

  template<class T1> 
  struct Y<T1, Unused>{}; // This is specialization of above, define it your way
};

int main()
{
  X<1>::Y<int> o1;  // Y<T1 = int, T2 = Unused> called
  X<2>::Y<int, float> o2; // Y<T1 = int, T2 = float> called
}

Here, however you can use X<1>, X<2> interchangeably. But in the broader example you mentioned, that is irrelevant. Still if you need, you can put checks for I = 1 and I = 2.

吻风 2024-11-08 14:49:08

您可以使用元函数(此处:内联 boost::mpl::if_c,但可以任意复杂)来选择您想要的函数。不过,您需要一些脚手架才能使用构造函数:

template <int I>
class X {
    template <typename T1>
    class YforIeq1 { /* meat of the class */ };
    template <typename T1, typename T2>
    class YforIeq2 { /* meat of the class */ };
public:
    template <typename T1, typename T2=boost::none_t/*e.g.*/>
    struct Y : boost::mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type {
        typedef typename mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type YBase;
        /* ctor forwarding: C++0x */
        using YBase::YBase;
        /* ctor forwarding: C++03 (runs into perfect fwd'ing problem)*/
        Y() : YBase() {}
        template <typename A1>
        Y(const A1&a1) : YBase(a1) {}
        template <typename A1, typename A2>
        Y(const A1&a1, const A2&a2) : YBase(a1,a2) {}
        // ...
    };
};

如果为每个 X 实例化两个 YforIeqN 时出现问题,那么您可以尝试将它们包装为 nullary元函数(沿 mpl::apply 的方式执行的操作)并使用 mpl::eval_if_c

You can use a meta function (here: inlined boost::mpl::if_c, but could be arbitrarily complex) to select the one you want. You need some scaffolding to be able to use constructors, though:

template <int I>
class X {
    template <typename T1>
    class YforIeq1 { /* meat of the class */ };
    template <typename T1, typename T2>
    class YforIeq2 { /* meat of the class */ };
public:
    template <typename T1, typename T2=boost::none_t/*e.g.*/>
    struct Y : boost::mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type {
        typedef typename mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type YBase;
        /* ctor forwarding: C++0x */
        using YBase::YBase;
        /* ctor forwarding: C++03 (runs into perfect fwd'ing problem)*/
        Y() : YBase() {}
        template <typename A1>
        Y(const A1&a1) : YBase(a1) {}
        template <typename A1, typename A2>
        Y(const A1&a1, const A2&a2) : YBase(a1,a2) {}
        // ...
    };
};

If there's a problem with both YforIeqN being instantiated for each X, then you can try wrapping them as a nullary meta function (something along the way mpl::apply does) and use mpl::eval_if_c.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文