用户通过定义指定所需选项的类来自定义库模板类。称之为清单。我们的想法是在清单中包含可选的typedef。例如,如果用户的清单包含H的typedef,我希望库代码使用指示的类型作为其“H”。如果用户清单中没有typedef,则库将使用默认值。
我怀疑使用新的C ++ 11功能有一种优雅的方法可以做到这一点,但我想要空洞了。我有一个解决方案,基于SFINAE的维基百科条目。这很难看。对于每个新的H,它需要一个新的模板函数 has_typedef_H 。我非常不安,它利用了 0 可以表示整数或空指针的属性。看起来太糟糕了。
有更好的方法吗?最好能在VC ++ 2010中使用吗?
在玩具示例中,有五个类,H1,H2和U0,U1和U2。 H1和H2是库类L的“助手”的示例.H1是默认值。 U是用户定义类的示例。在这个例子中,我省略了一个库类L,只是使用main()的主体来选择基于U的typedef(或缺少它)的H。 H2的主题。
struct H1{
void operator() (){ std::cout << "H1" << std::endl;}
};
struct H2{
void operator() (){ std::cout << "H2" << std::endl;}
};
struct default_H: public H1 {};
struct U2 {
typedef H2 H;
};
struct U1 {
typedef H1 H;
};
struct U0 {
};
template <typename T>
class has_typedef_H {
typedef char no[false+1];
typedef char yes[true+1];
template
static yes& test(typename C::H*);
template
static no& test(...);
public:
static const bool value = sizeof(test(0))-1;
};
template<typename U, bool >
struct type_H_B: public default_H{};
template<typename U>
struct type_H_B<U, true>: public U::H {};
template<typename U>
struct H_type: public type_H_B<U, has_typedef_H<U>::value> {};
int main() {
H_type<U0> h0;
H_type<U1> h1;
H_type<U2> h2;
// Prints H1 H1 H2
h0();
h1();
h2();
return 0;
}
答案 0 :(得分:3)
您并不需要为每个嵌套类型提供复杂的特征,这可以更简单一些。 Here就是一个例子:
// Helper to map any type to void, needed by SFINAE below
template <typename T>
struct void_type {
typedef void type;
};
// Selects a nested typedef or a default type D (using a macro to reduce boilerplate):
#define SELECT_NESTED_TYPE( TYPE ) \
template <typename T, typename D, typename _ = void> \
struct select_##TYPE{ \
typedef D type; \
}; \
template <typename T, typename D> \
struct select_##TYPE<T, D, typename void_type<typename T::TYPE>::type> { \
typedef typename T::TYPE type; \
};
SELECT_NESTED_TYPE( int_t );
SELECT_NESTED_TYPE( float_t );
//...
#undef SELECT_NESTED_TYPE
// Use
template <typename T>
class TheTemplate {
public:
typedef typename select_int_t<T,int>::type int_t;
typedef typename select_float_t<T,double>::type float_t;
//....
};
// Test:
template <typename T, typename U> struct same_type {
static const bool value = false;
};
template <typename T> struct same_type<T,T> {
static const bool value = true;
};
struct test1 {
};
struct test2 {
typedef long long int_t;
typedef float float_t;
};
int main() {
// test1 has the default typedefs
assert(( same_type< TheTemplate<test1>::int_t, int>::value ));
assert(( same_type< TheTemplate<test1>::float_t, double>::value ));
// test2 has the ones in the type
assert(( same_type< TheTemplate<test2>::int_t, long long>::value ));
assert(( same_type< TheTemplate<test2>::float_t, float>::value ));
}
如果宏采用默认类型并在默认情况下注入(当未定义嵌套类型时),则可以选择提供稍微简单的解决方案。不可否认,这需要为每个嵌套类型创建特征,但特征只是几行(并且不太难以定义为宏)。或者,如果只有几个潜在的typedef,您可以在没有额外样板的情况下进行,并直接在目标类型上使用SFINAE。
完全不同的方法......如果可以的话
如果您可以修改库中使用的类型,那么您可以通过滥用继承来使用更简单(但不是那么酷解决方案)。创建一个仅包含要使用的默认类型的typedef的基类,并使每个用户类派生自提供默认值的类。如果用户想要提供比默认帮助更好的帮助器,他们只需要提供typedef。如果他们没有提供typedef,查找将在层次结构中找到默认值:
struct default_helpers {
typedef Helper1 helper1_t;
typedef Helper2 helper2_t;
// ...
};
struct user_type_1 : default_helpers {
};
struct user_type_2 : default_helpers {
typedef MyHelper helper1_t; // I prefer this one...
};
int main() {
assert( same_type< user_type1::helper1_t, default_helpers::helper1_t >::value );
assert( !same_type< user_type2::helper1_t, default_helpers::helper1_t >::value );
assert( same_type< user_type1::helper2_t, user_type2::helper2_t>::value );
}
答案 1 :(得分:0)
正如大卫的answer所示,宏将有助于减少样板代码。在我看来,大卫的方法是优越的,但对于新的元程序员来说这可能是令人生畏的。因此,我想通过提供可能稍微更明确的潜在替代方案来帮助清理一些kludger。
当无案例不是模板时,0
会被视为int
:
template <typename T>
class has_typedef_H {
typedef char no[1];
typedef char yes[2];
template <typename C>
static yes& test(typename C::H*);
// template <typename>
static no& test(...);
public:
static const bool value = sizeof(yes) == sizeof(test(0));
};
在此example中:
C
被推断为int
,导致替换总是以int::H
失败。在尝试使用0
作为表示空指针的值之前会发生这种情况,因为函数签名仍在尝试识别。int
。这总是被选中,因为是的情况总是失败。当无案例变成模板函数时,没有模板参数可以推导出来,因此无案例不是候选者。
template <typename T>
class has_typedef_H {
typedef char no[1];
typedef char yes[2];
template <typename C>
static yes& test(typename C::H*);
template <typename>
static no& test(...);
public:
static const bool value = sizeof(yes) == sizeof(test(0));
};
如here所示,编译失败,因为找不到test(int)
的匹配函数。由于扣除不适用于无案例,test
调用必须明确选择要使用的专业化:
template <typename T>
class has_typedef_H {
typedef char no[1];
typedef char yes[2];
template <typename C>
static yes& test(typename C::H*);
template <typename>
static no& test(...);
public:
static const bool value = sizeof(yes) == sizeof(test<T>(0));
};
在此example中,0
的双重表示确实起作用。
0
被检查为T::H*
的有效初始化值。0
是int
。为了删除这种双重表示,将int
参数添加到yes-case,并为C::H*
参数提供默认值NULL
。
template <typename T>
class has_typedef_H {
typedef char no[1];
typedef char yes[2];
template <typename C>
static yes& test(int, typename C::H* = NULL);
template <typename>
static no& test(...);
public:
static const bool value = sizeof(yes) == sizeof(test<T>(1));
};
在此example中,可以使用任何整数调用test<T>()
。
1
是int
,C::H*
明确设置为NULL
。1
也是int
。因为sizeof
正在评估表达式而不执行它,所以可以避免将编译时已知的值传递给test
,如this示例所示。
template <typename T>
class has_typedef_H {
typedef char no[1];
typedef char yes[2];
template <typename C>
static yes& test(T*, typename C::H* = NULL);
template <typename>
static no& test(...);
static T* t;
public:
static const bool value = sizeof(yes) == sizeof(test<T>(t));
};
最后,我发现使用0
的案例在SFINAE中相当普遍。话虽如此,如果有人使用0
了解该方法,那么稍微更明确的方法应该不难理解。
答案 2 :(得分:0)
除了已在此处发布的好方法之外,另一种方法是使用很少使用的dominance in virtual inheritance feature。我正在展示两种可自定义类型的解决方案,可以扩展为任意数量的N(取决于基类编译器限制的数量):
#include <stdio.h>
struct A1 {};
struct A2 {};
struct B1 {};
struct B2 {};
struct DefaultManifest {
typedef A1 A;
typedef B1 B;
};
template<class T>
struct A_is : virtual DefaultManifest { typedef T A; };
template<class T>
struct B_is : virtual DefaultManifest { typedef T B; };
template<class T> struct Name { static char const* value; };
template<> char const* Name<A1>::value = "A1";
template<> char const* Name<A2>::value = "A2";
template<> char const* Name<B1>::value = "B1";
template<> char const* Name<B2>::value = "B2";
struct na1 : virtual DefaultManifest {};
struct na2 : virtual DefaultManifest {};
template<class T1 = na1, class T2 = na2>
struct Library {
struct Manifest : T1, T2 {};
typedef typename Manifest::A A;
typedef typename Manifest::B B;
Library() {
printf("A is %s, B is %s\n", Name<A>::value, Name<B>::value);
}
};
int main() {
Library<> lib1;
Library<A_is<A2> > lib2;
Library<B_is<B2> > lib3;
Library<A_is<A2>, B_is<B2> > lib4;
Library<B_is<B2>, A_is<A2> > lib5;
}
以上输出:
A is A1, B is B1
A is A2, B is B1
A is A1, B is B2
A is A2, B is B2
A is A2, B is B2