问题
I'm facing following problem:
I have some generic container, that is able to do some operations on types. The operations are, for simplicity, thread safe, when requested to. And, requested to means that the type in container has typedef std::true_type needs_thread_safety;
.
struct thread_safe_item {
typedef std::true_type needs_thread_safety;
/* */
};
struct thread_unsafe_item {
typedef std::false_type needs_thread_safety;
/* */
};
template<typename TItem> container {
/* some algorithms, that are std::enable_if selected, according to needs_thread_safety */
};
But, I want the needs_thread_safety
to be opt-in, and not needed to be defined (= default false_type). I've tried following:
struct thread_unsafe_item {
/* */
};
template<typename TItem>
struct thread_safety_selector
{
template<typename T>
struct has_defined_thread_safety
{
typedef char yes[1];
typedef char no[2];
template <typename C> static yes& test(typename C::needs_thread_safety*);
template <typename> static no& test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
typedef
typename std::conditional<
has_defined_thread_safety<TItem>::value,
typename TItem::needs_thread_safety,
std::false_type
>::type needs_thread_safety;
};
....
struct <typename TItem> container {
/* changed all TItem::needs_thread_safety selectors to thread_safety_selector<TItem>::needs_thread_safety */
};
But there is apparently no lazy-evaluation going on, as the error is error C2039: 'needs_thread_safety' : is not a member of 'thread_unsafe_item'
.
How can I achieve default value for the argument not specified?
It is for educational purposes, so I don't need different way how to solve this problem.
Thanks!
回答1:
You can't use std::conditional
for that, as that will always parse all of its arguments. You can create your own predicate:
template <bool, class>
struct get_thread_safety;
template <class T>
struct get_thread_safety<true, T>
{
typedef typename T::needs_thread_safety type;
};
template <class T>
struct get_thread_safety<false, T>
{
typedef std::false_type type;
};
// Used like this:
typedef
typename get_thread_safety<
has_defined_thread_safety<TItem>::value,
TItem
>::type needs_thread_safety;
回答2:
Actually this can be done a bit easier using std::enable_if
coupled with std::conditional
:
#include <iostream>
#include <type_traits>
// Classes to be checked against needs_thread_safety
struct A {};
struct B { typedef std::true_type needs_thread_safety; };
struct C { typedef std::false_type needs_thread_safety; };
// Checker helper
template<class T> class get_thread_safety
{
typedef char(&zero_size_t)[0];
template <class X> static typename std::enable_if<X::needs_thread_safety::value, char>::type check(int);
template <class X> static typename std::enable_if<!X::needs_thread_safety::value, zero_size_t>::type check(int);
template <class X> static zero_size_t check(...);
public:
typedef typename std::conditional<sizeof(check<T>(0)), std::true_type, std::false_type>::type type;
};
int main()
{
// Usage. Will print 0 1 0
std::cout << get_thread_safety<A>::type::value << std::endl;
std::cout << get_thread_safety<B>::type::value << std::endl;
std::cout << get_thread_safety<C>::type::value << std::endl;
return 0;
}
回答3:
You can actually use std::conditional
in a lazy way, you just need to avoid to use the nested typedef inside its template parameter list. You'll also need another fallback type for the false
case ("default value", as you called it):
template<typename TItem>
struct thread_safety_selector
{
template<typename T>
struct has_defined_thread_safety
{
typedef char yes[1];
typedef char no[2];
template <typename C> static yes& test(typename C::needs_thread_safety*);
template <typename> static no& test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
// this would be your "default value"
struct not_thread_safe {
typedef std::false_type needs_thread_safety;
};
typedef
typename std::conditional<
has_defined_thread_safety<TItem>::value,
TItem, // <---- note, not using the typedef here
not_thread_safe
>::type::needs_thread_safety needs_thread_safety;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// lazily, after the selection is done
};
来源:https://stackoverflow.com/questions/21602715/how-to-use-default-value-within-template-metaprogramming