Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++和Type Mult:最简单的定义可能定义列表的方法_C++_Templates_Typetraits - Fatal编程技术网

C++和Type Mult:最简单的定义可能定义列表的方法

C++和Type Mult:最简单的定义可能定义列表的方法,c++,templates,typetraits,C++,Templates,Typetraits,我想定义一个函数模板T constScriptVar\u T&s;。根据T,我想有不同的定义。ScriptVar\u t是一个类,但在此上下文中,细节并不重要 T上的条件不像特定类型那么简单,它们都是更复杂的静态布尔表达式。也就是说,我有一个表达式列表ext1..extN,对于每个表达式,我都有一个该函数的定义。我希望按照这个顺序检查它们,并且应该使用第一个匹配表达式的定义。如果它们都失败了,我想得到一个编译器错误 现在,我只有2个定义,我的代码看起来像这是一个完整的测试用例,相关的代码被标记为

我想定义一个函数模板T constScriptVar\u T&s;。根据T,我想有不同的定义。ScriptVar\u t是一个类,但在此上下文中,细节并不重要

T上的条件不像特定类型那么简单,它们都是更复杂的静态布尔表达式。也就是说,我有一个表达式列表ext1..extN,对于每个表达式,我都有一个该函数的定义。我希望按照这个顺序检查它们,并且应该使用第一个匹配表达式的定义。如果它们都失败了,我想得到一个编译器错误

现在,我只有2个定义,我的代码看起来像这是一个完整的测试用例,相关的代码被标记为:

#include <boost/type_traits.hpp>

enum {
    SVT_INT,
    SVT_FLOAT,
    SVT_BASEOBJ,
    SVT_CUSTOMVAR
};

struct BaseObject {};
struct CustomVar {};

template<typename T> struct GetType;
template<> struct GetType<int> { static const int value = SVT_INT; };
template<> struct GetType<float> { static const int value = SVT_FLOAT; };
template<> struct GetType<BaseObject> { static const int value = SVT_BASEOBJ; };

template<bool> struct GetType_BaseCustomVar;
template<> struct GetType_BaseCustomVar<true> {
    struct Type { static const int value = SVT_CUSTOMVAR; };
};
template<typename T> struct GetType : GetType_BaseCustomVar<boost::is_base_of<CustomVar,T>::value>::Type {};

struct ScriptVar_t;
template<typename T> T CastScriptVarConst(const ScriptVar_t& s);

struct ScriptVar_t {
    operator int() const { return 0; }
    operator float() const { return 0.0f; }
    operator BaseObject() const { return BaseObject(); }
    template<typename T> T* as() const { return NULL; }

    template <typename T> T castConst() const { return CastScriptVarConst<T>(*this); }
};

// *** relevant code starts here

template<typename T> T CastScriptVarConst(const ScriptVar_t& s);

template<bool> struct CastScriptVar1;
template<typename T> struct CastScriptVar1_IsSimpleType {
    static const bool value = GetType<T>::value < SVT_BASEOBJ;
};
template<> struct CastScriptVar1<true> {
    template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) { return (T) s; }
};

template<bool> struct CastScriptVar2;
template<typename T> struct CastScriptVar2_IsCustomVar {
    static const bool value = boost::is_base_of<CustomVar,T>::value;
};
template<> struct CastScriptVar2<true> {
    template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) { return *s.as<T>(); }
};

template<> struct CastScriptVar1<false> {
    template<typename T> static T castConst(const ScriptVar_t& s, const T& /*dummy*/) {
        return CastScriptVar2<CastScriptVar2_IsCustomVar<T>::value>::castConst(s, T());
    }
};
template<typename T> T CastScriptVarConst(const ScriptVar_t& s) {
    return CastScriptVar1<CastScriptVar1_IsSimpleType<T>::value>::castConst(s, T());
}

int main() {
    ScriptVar_t v;
    v.castConst<int>();
    v.castConst<CustomVar>();
}
我试了几次才想到这个,直到它起作用

从代码中可以看到,这两个表达式是GetType::value 我想知道对于这段代码是否有更干净的解决方案


作为参考,我正在玩它。现在,我又有了一个与所有其他解决方案略有不同的解决方案。

我不确定我是否正确理解您的意思,但这个元程序可以满足您的需要:

// Just a placeholder type for default template arguments
struct NoType { };

// Template to check whether a type is NoType. You can replace this with boost
// is_same<T, NoType> if you like.
template <typename T>
struct IsNoType  {
    static const bool value = false;
};

template <>
struct IsNoType<NoType>  {
    static const bool value = true;
};


// Template for matching the expressions and their corresponding types
// This could be done more nicely using variadic templates but no MSVC
// version supports them currently.
// You can specify up to 8 conditions and types. If you specify more,
// the code will break :) You can add more easily by just expanding the
// number of lines and parameters though.
template <
    bool p0 = false, typename t0 = NoType,
    bool p1 = false, typename t1 = NoType,
    bool p2 = false, typename t2 = NoType,
    bool p3 = false, typename t3 = NoType,
    bool p4 = false, typename t4 = NoType,
    bool p5 = false, typename t5 = NoType,
    bool p6 = false, typename t6 = NoType,
    bool p7 = false, typename t7 = NoType,

    // This must not be changed/overriden/specified, it is used as a condition to stop the compiler loop, see below
    bool stop = true, typename stopT = NoType  
>
struct GetFirstMatchingType  {

};

// Specialization when the first element in the expression list is true.
// When this happens, we just return the first type as the ::type typedef.
template <
    typename t0,
    bool p1, typename t1,
    bool p2, typename t2,
    bool p3, typename t3,
    bool p4, typename t4,
    bool p5, typename t5,
    bool p6, typename t6,
    bool p7, typename t7,
    bool p8, typename t8
>
struct GetFirstMatchingType<true, t0, p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8>  {
    typedef t0 type;

    // Check that the type is not NoType. If it is, it means all arguments are false and we should fail.
    // In case of a non-C++11 compiler, you can throw any other compiler error here or use BOOST_STATIC_ASSERT
    static_assert(!IsNoType<t0>::value, "No expression has been matched, don't know what type to return!");
};

// Specialization when the first type is false. If this happens, we cyclically rotate
// the sequence so that p0, t0 becomes p8, t8. The compiler keeps expanding this
// until it finds true as the first element. Note that this will always happen because
// the stop argument in the base template is set to true.
template <
    typename t0,
    bool p1, typename t1,
    bool p2, typename t2,
    bool p3, typename t3,
    bool p4, typename t4,
    bool p5, typename t5,
    bool p6, typename t6,
    bool p7, typename t7,
    bool p8, typename t8
>
struct GetFirstMatchingType<false, t0, p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8>  {
    typedef typename GetFirstMatchingType<p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8, false, t0>::type type;
};

int main()  {

// Evaluates to int myVar1 if int is 4 bytes, or __int32 myVar1 if __int32 is 4 bytes and int is not 4 bytes
GetFirstMatchingType<
    sizeof(int) == 4, int,
    sizeof(__int32) == 4, __int32
>::type myVar1;

// Evaluates to short myVar on my platform
GetFirstMatchingType<
    sizeof(int) == 5, int,
    sizeof(short) == 2, short
>::type myVar2;

// Also evaluates to short myVar on my platform
GetFirstMatchingType<
    sizeof(int) == 5, int,
    sizeof(short) == 2, short,
    sizeof(int) == 4, int
>::type myVar3;

// Throws an error (error C2338: No expression has been matched, don't know what type to return!)
GetFirstMatchingType<
    sizeof(int) == 5, int,
    sizeof(long) == 5, long,
    sizeof(short) == 3, short
>::type myVar4;
}
在MSVC 2010上进行测试,但它应该在任何符合C++的编译器(如GCC或CLAN.< /P>)上都能很好地工作。 编辑 以下是使用上述代码解决问题的示例:

struct ScriptVar_t;

struct CastScriptVar1 {
    template<typename T> static T castConst(const ScriptVar_t& s) { return (T) s; }
};

struct CastScriptVar2 {
    template<typename T> static T castConst(const ScriptVar_t& s) { return *s.as<T>(); }
};

struct ScriptVar_t {
    operator int() const { return 0; }
    operator float() const { return 0.0f; }
    operator BaseObject() const { return BaseObject(); }
    template<typename T> T* as() const { return NULL; }

    template <typename T> T castConst() const { 
        return GetFirstMatchingType<
            !boost::is_base_of<CustomVar, T>::value, CastScriptVar1,
            GetType<T>::value >= SVT_BASEOBJ, CastScriptVar2
            // Add more conditions & casts here
        >::type::castConst<T>(*this);
    }
};

int main()
{
    ScriptVar_t v;
    v.castConst<int>();
    v.castConst<CustomVar>();

    return 0;
}
可以使用boost::tuple和boost::mpl重写它,以消除奇怪的可变模板


编辑2:我以前的编辑好像消失了,我把它放回去了。我不确定我是否理解正确,但这个元程序可以做你需要的事情:

// Just a placeholder type for default template arguments
struct NoType { };

// Template to check whether a type is NoType. You can replace this with boost
// is_same<T, NoType> if you like.
template <typename T>
struct IsNoType  {
    static const bool value = false;
};

template <>
struct IsNoType<NoType>  {
    static const bool value = true;
};


// Template for matching the expressions and their corresponding types
// This could be done more nicely using variadic templates but no MSVC
// version supports them currently.
// You can specify up to 8 conditions and types. If you specify more,
// the code will break :) You can add more easily by just expanding the
// number of lines and parameters though.
template <
    bool p0 = false, typename t0 = NoType,
    bool p1 = false, typename t1 = NoType,
    bool p2 = false, typename t2 = NoType,
    bool p3 = false, typename t3 = NoType,
    bool p4 = false, typename t4 = NoType,
    bool p5 = false, typename t5 = NoType,
    bool p6 = false, typename t6 = NoType,
    bool p7 = false, typename t7 = NoType,

    // This must not be changed/overriden/specified, it is used as a condition to stop the compiler loop, see below
    bool stop = true, typename stopT = NoType  
>
struct GetFirstMatchingType  {

};

// Specialization when the first element in the expression list is true.
// When this happens, we just return the first type as the ::type typedef.
template <
    typename t0,
    bool p1, typename t1,
    bool p2, typename t2,
    bool p3, typename t3,
    bool p4, typename t4,
    bool p5, typename t5,
    bool p6, typename t6,
    bool p7, typename t7,
    bool p8, typename t8
>
struct GetFirstMatchingType<true, t0, p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8>  {
    typedef t0 type;

    // Check that the type is not NoType. If it is, it means all arguments are false and we should fail.
    // In case of a non-C++11 compiler, you can throw any other compiler error here or use BOOST_STATIC_ASSERT
    static_assert(!IsNoType<t0>::value, "No expression has been matched, don't know what type to return!");
};

// Specialization when the first type is false. If this happens, we cyclically rotate
// the sequence so that p0, t0 becomes p8, t8. The compiler keeps expanding this
// until it finds true as the first element. Note that this will always happen because
// the stop argument in the base template is set to true.
template <
    typename t0,
    bool p1, typename t1,
    bool p2, typename t2,
    bool p3, typename t3,
    bool p4, typename t4,
    bool p5, typename t5,
    bool p6, typename t6,
    bool p7, typename t7,
    bool p8, typename t8
>
struct GetFirstMatchingType<false, t0, p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8>  {
    typedef typename GetFirstMatchingType<p1, t1, p2, t2, p3, t3, p4, t4, p5, t5, p6, t6, p7, t7, p8, t8, false, t0>::type type;
};

int main()  {

// Evaluates to int myVar1 if int is 4 bytes, or __int32 myVar1 if __int32 is 4 bytes and int is not 4 bytes
GetFirstMatchingType<
    sizeof(int) == 4, int,
    sizeof(__int32) == 4, __int32
>::type myVar1;

// Evaluates to short myVar on my platform
GetFirstMatchingType<
    sizeof(int) == 5, int,
    sizeof(short) == 2, short
>::type myVar2;

// Also evaluates to short myVar on my platform
GetFirstMatchingType<
    sizeof(int) == 5, int,
    sizeof(short) == 2, short,
    sizeof(int) == 4, int
>::type myVar3;

// Throws an error (error C2338: No expression has been matched, don't know what type to return!)
GetFirstMatchingType<
    sizeof(int) == 5, int,
    sizeof(long) == 5, long,
    sizeof(short) == 3, short
>::type myVar4;
}
在MSVC 2010上进行测试,但它应该在任何符合C++的编译器(如GCC或CLAN.< /P>)上都能很好地工作。 编辑 以下是使用上述代码解决问题的示例:

struct ScriptVar_t;

struct CastScriptVar1 {
    template<typename T> static T castConst(const ScriptVar_t& s) { return (T) s; }
};

struct CastScriptVar2 {
    template<typename T> static T castConst(const ScriptVar_t& s) { return *s.as<T>(); }
};

struct ScriptVar_t {
    operator int() const { return 0; }
    operator float() const { return 0.0f; }
    operator BaseObject() const { return BaseObject(); }
    template<typename T> T* as() const { return NULL; }

    template <typename T> T castConst() const { 
        return GetFirstMatchingType<
            !boost::is_base_of<CustomVar, T>::value, CastScriptVar1,
            GetType<T>::value >= SVT_BASEOBJ, CastScriptVar2
            // Add more conditions & casts here
        >::type::castConst<T>(*this);
    }
};

int main()
{
    ScriptVar_t v;
    v.castConst<int>();
    v.castConst<CustomVar>();

    return 0;
}
可以使用boost::tuple和boost::mpl重写它,以消除奇怪的可变模板


编辑2:我以前的编辑好像消失了,我把它放回原处,所以如果我理解正确,你有两种铸造方法。如果GetType::value 另一方面,如果GetType::value 否则,您需要一个编译错误

这里有一种使用重载和std::enable_的方法,如果:


如果我理解正确,你有两种铸造方法。如果GetType::value 另一方面,如果GetType::value 否则,您需要一个编译错误

这里有一种使用重载和std::enable_的方法,如果:


如果我理解正确,我会使用查找表 将算符和元函数强制转换为 桌子

另一种方法是使用基于类型的查找表,其中包括 标记和函子的集合。pick_cast会选择正确的标签 而不是int。如果决定 桌子变大了

#include <boost/type_traits.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/at.hpp>

struct BaseObject {};
struct CustomVar {};

namespace mpl = boost::mpl;

struct ScriptVar_t {
    operator int() const { return 0; }
    operator float() const { return 0.0f; }
    operator BaseObject() const { return BaseObject(); }
    template<typename T> T* as() const { return NULL; }
    template <typename T> T castConst() const;
};

struct default_cast {
  template<typename T>
  T operator()(const ScriptVar_t& s) const { return (T) s; }
};

struct base_cast {
  template<typename T>
  T operator()(const ScriptVar_t& s) const { return *s.as<T>(); }
};

typedef mpl::vector< default_cast, base_cast > casts;

enum {
  DEFAULT = 0,
  BASE,
  END_OF_ENUM
};

// pick the right cast for T
template<typename T>
struct pick_cast {
  typedef typename mpl::if_< typename boost::is_base_of<CustomVar,T>::type,
                             mpl::int_<BASE>, mpl::int_<DEFAULT> >::type type;
};

template <typename T> T ScriptVar_t::castConst() const { 
  typedef typename mpl::at<casts, typename pick_cast<T>::type>::type func;
  return func().template operator()<T>(*this);
}

int main() {
    ScriptVar_t v;
    v.castConst<int>();
    v.castConst<CustomVar>();
}

如果我理解正确,我会使用查找表 将算符和元函数强制转换为 桌子

另一种方法是使用基于类型的查找表,其中包括 标记和函子的集合。pick_cast会选择正确的标签 而不是int。如果决定 桌子变大了

#include <boost/type_traits.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/at.hpp>

struct BaseObject {};
struct CustomVar {};

namespace mpl = boost::mpl;

struct ScriptVar_t {
    operator int() const { return 0; }
    operator float() const { return 0.0f; }
    operator BaseObject() const { return BaseObject(); }
    template<typename T> T* as() const { return NULL; }
    template <typename T> T castConst() const;
};

struct default_cast {
  template<typename T>
  T operator()(const ScriptVar_t& s) const { return (T) s; }
};

struct base_cast {
  template<typename T>
  T operator()(const ScriptVar_t& s) const { return *s.as<T>(); }
};

typedef mpl::vector< default_cast, base_cast > casts;

enum {
  DEFAULT = 0,
  BASE,
  END_OF_ENUM
};

// pick the right cast for T
template<typename T>
struct pick_cast {
  typedef typename mpl::if_< typename boost::is_base_of<CustomVar,T>::type,
                             mpl::int_<BASE>, mpl::int_<DEFAULT> >::type type;
};

template <typename T> T ScriptVar_t::castConst() const { 
  typedef typename mpl::at<casts, typename pick_cast<T>::type>::type func;
  return func().template operator()<T>(*this);
}

int main() {
    ScriptVar_t v;
    v.castConst<int>();
    v.castConst<CustomVar>();
}

你能让你的表达式解析为唯一的整数,并用int-parm代替bool进行专门化,并给非专门化一个错误的例子吗?我觉得解决方案是尝试在第二维度上扩展选择,而不是仅仅扩展“bool”空间本身。@Joe:好的,当然,但那真的更简单吗?这似乎更为复杂。或者你会怎么做?也许您可以给出一些代码示例?请注意,传递一个伪值会给T添加一个可默认构造的约束,您可能希望避免这种情况,或者使用declval来获取一个值。您是否可以使表达式解析为唯一整数,并使用int parm来进行专门化而不是bool,并为非专门化提供错误情况?我觉得解决方案是尝试在第二维度上扩展选择,而不是仅仅扩展“bool”空间本身。@Joe:好的,当然,但那真的更简单吗?这似乎更为复杂。或者你会怎么做?也许您可以给出一些代码示例?请注意,传递一个虚拟对象会添加一个可构造的默认值
约束为T,您可能希望避免此情况,或者使用declval来获取值。不,这不是我真正想要的。我想要一些与给定代码等价的东西,只是更简单而已。我的类型特征并不是简单地匹配一个类型,否则整个问题将是微不足道的,它们可能匹配一组可能无限的类型。此外,您的代码似乎并不比我给出的代码简单这正是我所寻找的:一些比我现在拥有的更简单的解决方案。我想这是一个相互误解:我编辑了我的帖子,在你的问题中加入了代码的替代方案。更一般的是,您可以添加多个约束/强制转换,而无需手动指定委托。我认为解决方案非常简单,只有一个结构和两个专门化。丑陋的部分是伪变量模板,可以使用boost::tuple和boost::mpl消除这些模板。然而,对于这样一个简单的编译器循环,我认为MPL是一种过分的杀伤力。不,这不是我真正想要的。我想要一些与给定代码等价的东西,只是更简单而已。我的类型特征并不是简单地匹配一个类型,否则整个问题将是微不足道的,它们可能匹配一组可能无限的类型。此外,您的代码似乎并不比我给出的代码简单这正是我所寻找的:一些比我现在拥有的更简单的解决方案。我想这是一个相互误解:我编辑了我的帖子,在你的问题中加入了代码的替代方案。更一般的是,您可以添加多个约束/强制转换,而无需手动指定委托。我认为解决方案非常简单,只有一个结构和两个专门化。丑陋的部分是伪变量模板,可以使用boost::tuple和boost::mpl消除这些模板。然而,对于这样一个简单的编译器循环,我认为MPL是一种过分的杀伤力。啊,这样更好但现在有一个问题。我必须预先声明它,因为我在ScriptVar\t中的另一个模板函数中使用此函数。我的预声明看起来像模板T CastScriptVarConst ScriptVar\u T&s;。然后我得到了一堆未找到的链接器错误符号。@Albert我认为您需要做的就是确保在实例化使用CastScriptBarConst的模板时CastScriptBarConst的定义可用。不过我不确定。如果你能在帖子中添加一个最小的测试用例来重现你的错误,也许我能提供更多帮助。我将问题中的代码更新为完整的测试用例。当我使用您的代码时,使用Clang会出现不明确的编译器错误,使用GCC会出现一些链接器错误。因此我使用了Booo::EnabLeIfiFiC和Boosi::iSuxBasic代替C++ STL变体。HM,OK,但是现在它看起来不再简单了。特别是,我不喜欢把所有不同的版本都预先声明出来。另外,我也不太喜欢在一个新的规范中添加所有先前表达式的否定。当我有2个或3个以上的表达式时,这会变得非常难看。@Albert你不必完全否定前面的条件,只要它们是相互排斥的。事实上,如果您只是直接从您的设计中转换条件,那么编译器将在您尝试使用的任何情况下对其进行验证,并且无论设计哪里出错,它都会发出一个关于不明确重载的难以理解的错误。最后,前向声明强制转换模板的另一种方法是在CastScriptVarConst的定义之后为ScriptVar_t的castConst模板成员函数提供定义。我会更新我的答案。啊,这样更好但现在有一个问题。我必须预先声明它,因为我在ScriptVar\t中的另一个模板函数中使用此函数。我的预声明看起来像模板T CastScriptVarConst ScriptVar\u T&s;。然后我得到了一堆未找到的链接器错误符号。@Albert我认为您需要做的就是确保在实例化使用CastScriptBarConst的模板时CastScriptBarConst的定义可用。不过我不确定。如果你能在帖子中添加一个最小的测试用例来重现你的错误,也许我能提供更多帮助。我将问题中的代码更新为完整的测试用例。当我使用您的代码时,使用Clang会出现不明确的编译器错误,使用GCC会出现一些链接器错误。因此我使用了Booo::EnabLeIfiFiC和Boosi::iSuxBasic代替C++ STL变体。HM,OK,但是现在它看起来不再简单了。特别是,我不喜欢把所有不同的版本都预先声明出来。另外,我也不太喜欢在一个新的规范中添加所有先前表达式的否定。当我有2个或3个以上的表达式时,这会变得非常难看。@Albert你不必完全否定前面的条件,只要它们是相互排斥的。事实上,如果您只是直接从设计中转换条件,那么编译器将在您尝试使用的任何情况下对其进行验证,并且无论设计哪里出错,它都将发出
n关于不明确重载的不可理解错误。最后,前向声明强制转换模板的另一种方法是在CastScriptVarConst的定义之后为ScriptVar_t的castConst模板成员函数提供定义。我将更新我的答案。我真的不认为需要查找表enum和mpl::vector。pick_cast可能已经返回了cast functor本身,不是吗?否则,我真的很喜欢这个解决方案@阿尔伯特完全正确。我可能会争辩说,使用mpl::vector为玩预处理器把戏打开了空间,但这可能不是您所需要的。我真的不认为需要查找表enum和mpl::vector。pick_cast可能已经返回了cast functor本身,不是吗?否则,我真的很喜欢这个解决方案@阿尔伯特完全正确。我可能会争辩说,使用mpl::vector为玩预处理器把戏打开了空间,但这可能不是您所需要的。