【问题标题】:Use of enable_if to match only classes which have a certain static data member, and that has only specific values使用 enable_if 仅匹配具有特定静态数据成员且仅具有特定值的类
【发布时间】:2018-09-29 05:29:12
【问题描述】:

我想为一个类的子集专门化一个函数:具有某个静态数据成员变量,并且该变量只有某些可能的值。

下面的代码说明了意图,但除非我注释掉main 中与B 类相关的行,否则它不会编译。这是因为code 不是Bx 类的成员,但如果模板参数具有code 成员变量,则enable_if 条件有效。应该怎么修改?

不幸的是,我使用的是非常旧的编译器,因此不支持 C++11:我使用选项 -std=c++03 进行编译。

谢谢

#include <iostream>
#include <boost/core/enable_if.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/bool.hpp>
using std::cout;
using namespace boost;
using namespace boost::mpl;

template <int N> struct A1 { static const int code = N; };
template <int N> struct A2 { static const int code = N; };
// ... other classes with static data member 'code'
template <int N> struct AN { static const int code = N; };

struct B1{};
struct B2{};
// ... other classes potentially passd as argument to the foo function
struct BN{};

template <typename T>
struct Condition : or_<bool_<T::code == 1>, bool_<T::code == 2> > {};


template <typename T>
typename enable_if<not_<Condition<T> >, void>::type
   foo(const T& arg) { cout << "This class does not have a static member code or its value is not 1 or 2\n"; }

template <typename T>
typename enable_if<Condition<T>, void>::type
   foo(const T& arg) { cout << "This class has a static member code and its value is " << T::code << "\n"; }

int main()
{
    foo(A1<0>()); // this should match the 1st version of foo
    foo(A2<1>()); // this should match the 2nd version of foo
    foo(AN<2>()); // this should match the 2nd version of foo
    foo(B1());    // this should match the 1st version of foo
    foo(BN());    // this should match the 1st version of foo
}

【问题讨论】:

  • 为什么你的代码不起作用?
  • 因为code 不是B 类的成员,并且它用于enable_if 的实例化。我已经更新了问题。
  • 如果你不会使用C++11,我想你必须写foo(B1()),而不是foo(B1{})
  • @max66,谢谢,我更新了问题。

标签: c++ templates boost template-meta-programming template-specialization


【解决方案1】:

真正的问题是您需要 C++03 解决方案,因此您可以使用 SFINAE,但不能使用从 C++11 开始的所有语言改进。

无论如何,我建议您提供一个与您的一样复杂(可能更多)但完全免费的解决方案。

如果你定义了一个简单的 bool 包装器(可以大致替代 C++11 的 std::true_typestd::false_type

template <bool B>
struct bool_wrapper
 { static const bool value = B; };

您可以如下定义您的条件

template <typename, typename = bool_wrapper<true> >
struct cond : public bool_wrapper<false>
 { };

template <typename T>
struct cond<T, bool_wrapper<(1 == T::code) || (2 == T::code)> >
   : public bool_wrapper<true>
 { };

如果你定义了一个 enable_if 类型的特征(与 C++11 的 std::enable_if 相同)

template <bool, typename = void>
struct enable_if
 { };

template <typename T>
struct enable_if<true, T>
 { typedef T type; };

您可以 SFINAE 启用/禁用您的 foo() 函数

template <typename T>
typename enable_if<false == cond<T>::value>::type foo (T const & arg)
 { std::cout << "no static member code or value not 1 and not 2\n"; }

template <typename T>
typename enable_if<true == cond<T>::value>::type foo (T const & arg)
 { std::cout << "static member code and its value is " << T::code << "\n"; }

以下是一个完整的 C++98 示例

#include <iostream>

template <int N> struct A1 { static const int code = N; };
template <int N> struct A2 { static const int code = N; };
// ... 
template <int N> struct AN { static const int code = N; };

struct B1{};
struct B2{};
// ...
struct BN{};

template <bool B>
struct bool_wrapper
 { static const bool value = B; };

template <typename, typename = bool_wrapper<true> >
struct cond : public bool_wrapper<false>
 { };

template <typename T>
struct cond<T, bool_wrapper<(1 == T::code) || (2 == T::code)> >
   : public bool_wrapper<true>
 { };

template <bool, typename = void>
struct enable_if
 { };

template <typename T>
struct enable_if<true, T>
 { typedef T type; };


template <typename T>
typename enable_if<false == cond<T>::value>::type foo (T const & arg)
 { std::cout << "no static member code or value not 1 and not 2\n"; }

template <typename T>
typename enable_if<true == cond<T>::value>::type foo (T const & arg)
 { std::cout << "static member code and its value is " << T::code << "\n"; }

int main ()
 {
   foo(A1<0>()); // match the 1st version of foo
   foo(A2<1>()); // match the 2nd version of foo
   foo(AN<2>()); // match the 2nd version of foo
   foo(B1());    // match the 1st version of foo
   foo(BN());    // match the 1st version of foo
 }

我不知道你使用的 boost 类,但我想你可以修改你的代码(几乎就像我的 no-boost 解决方案一样工作)如下

template <typename, typename = bool_<true> >
struct Condition : public bool_<false>
 { };

template <typename T>
struct Condition<T, bool_<(1 == T::code) || (2 == T::code)> >
   : public bool_<true>
 { };

-- 编辑--

OP 询问

我不明白 A1 的情况如何。 Condition 的特化应该是首选匹配,第二个参数扩展为 bool_。该类继承自 bool_,因此,它应该选择错误的 foo 版本。但是它有效。怎么可能?

嗯...当您编写foo(A1&lt;0&gt;()) 时,编译器必须了解cond&lt;A1&lt;0&gt;&gt;::valuetrue 还是false 才能启用foo() 的第一个版本或第二个版本。

所以编译器必须实现cond&lt;A1&lt;0&gt;&gt;。但是没有一个cond 模板类只接收一个类型名。无论如何,编译器发现

template <typename, typename = bool_wrapper<true> >
struct cond;

使用第二个模板参数的默认值匹配。

没有选择,所以没有歧义,所以cond&lt; A&lt;1&gt; &gt;变成cond&lt; A&lt;1&gt;, bool_wrapper&lt;true&gt; &gt;

现在编译器必须在主版本cond&lt;typename, typename&gt;(继承自bool_wrapper&lt;false&gt;)和特化版本(继承自bool_wrapper&lt;true&gt;)之间进行选择。

cond&lt; A&lt;1&gt;, bool_wrapper&lt;true&gt; &gt; 肯定匹配主版本,但也匹配专业?如果也匹配特化,编译器必须优先选择特化。

所以我们需要查看cond&lt; A&lt;0&gt;, bool_wrapper&lt;true&gt; &gt; 是否匹配特化。

A&lt;0&gt; 用作T,我们就有了专业化

cond< A<0>, bool_wrapper<(1 == A<0>::code) || (2 == A<0>::code)> >

那是

cond< A<0>, bool_wrapper<(1 == 0) || (2 == 0)> >

那是

cond< A<0>, bool_wrapper<false || false> >

那是

cond< A<0>, bool_wrapper<false> >

这与cond&lt; A&lt;0&gt;, bool_wrapper&lt;true&gt; &gt; 不匹配。

所以cond&lt; A&lt;0&gt; &gt;,也就是cond&lt; A&lt;0&gt;, bool_wrapper&lt;true&gt; &gt;,只匹配cond&lt;typename, typename&gt;的主版本,所以继承自bool_wrapper&lt;false&gt;

现在我们可以看看cond&lt; A&lt;1&gt; &gt;

对于cond&lt; A&lt;0&gt; &gt;,唯一匹配cond&lt; A&lt;1&gt; &gt;的模板condcond&lt;typename, typename&gt;,第二个typename是默认值。

所以cond&lt; A&lt;1&gt; &gt;cond&lt; A&lt;1&gt;, bool_wrapper&lt;true&gt; &gt;

但是cond&lt; A&lt;1&gt;, bool_wrapper&lt;true&gt; &gt; 只匹配cond&lt;typename, typename&gt; 的主要版本还是也匹配专业化?

我们可以看到,使用A&lt;1&gt; 作为T,我们就有了特化变成了

cond< A<1>, bool_wrapper<(1 == A<1>::code) || (2 == A<1>::code)> >

那是

cond< A<1>, bool_wrapper<(1 == 1) || (2 == 1)> >

那是

cond< A<1>, bool_wrapper<true || false> >

那是

cond< A<1>, bool_wrapper<true> >

这匹配cond&lt; A&lt;1&gt;, bool_wrapper&lt;true&gt; &gt;

所以,对于cond&lt; A&lt;1&gt; &gt;,又名cond&lt; A&lt;1&gt;, bool_wrapper&lt;true&gt;cond&lt;typename, typename&gt; 的两个版本都匹配,所以编译器必须选择特化,所以cond&lt; A&lt;1&gt; &gt; 继承自bool_wrapper&lt;true&gt;

【讨论】:

  • 这很好。它避免了将条件分成两部分,因为静态数据成员的存在被隐式检查。我不明白它在A1&lt;0&gt; 的情况下是如何工作的。 Condition 的特化应该是首选匹配,第二个参数扩展为 bool_&lt;false&gt;。该类继承自bool_&lt;true&gt;,因此,它应该选择错误版本的foo。但是它有效。这怎么可能?
  • @Fabio - 改进了答案。希望这会有所帮助。
  • 感谢您的回答。我将它标记为正确,因为它有效,并且比我的更优雅。关于我的进一步问题,我没有正确表达,因为评论空间不大。因此,我认为进一步的解释没有解决这个问题。所以我在这里开了一个新问题stackoverflow.com/questions/52583833/…。您可以考虑从该答案中删除额外的详细信息并回复该答案。
【解决方案2】:

根据 J. Zwinck 的建议,我得到了它的工作。

将这两个条件放在and_template 类中不起作用,原因与上述相同。但是,我可以将第一个条件(类具有成员变量 code)放在 enable_if 中,然后将第二个条件(代码具有特定值)放在类中(参见下面的示例)。

If 有点令人费解。如果有人能提出更优雅的解决方案,我会接受。

#include <boost/tti/has_static_member_data.hpp>

BOOST_TTI_HAS_STATIC_MEMBER_DATA(code)

template <typename T, typename Enable = void>
struct Condition : false_type {};;

template <typename T>
struct Condition<T, typename enable_if<bool_<has_static_member_data_code<T,const int>::value> >::type>
{
    typedef or_<bool_<T::code == 1>, bool_<T::code == 2> > type;
    const static bool value = type::value;
};

【讨论】:

    猜你喜欢
    • 2021-01-29
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多