假设template <class T> class MyThing
包含大量using
语句,例如using T::my_def
或using T::my_other_def
。出于显而易见的原因,如果T
未定义my_def
和my_other_def
,则编译将失败。 然而,实际用例中的编译失败导致数百个错误,使用户难以理解真正的问题。虽然编译失败已经足够好了,但我真的很想通过某种聪明的代理类找到一种方法来提前终止编译。首先,为了完整性,包括SFINAE设置。第二个是如何使用函数完成此操作的必然结果示例。然后我的尝试,并怀疑为什么这可能实际上不可能(但我希望是错的)。
注意:这是严格的&#34;&#34; C ++ 11项目(但我并不反对导入类型,例如std::void_t
已成功,但我在std::conditional
失败了。如果您了解这些内容,请使用note:
is_valid<T>()
是static constexpr bool
函数,其行为与std::conditional
类似,检查my_def
和my_other_def
是T
中的有效类型名称。 ///////////////////////////////////////////////////////
// backport from C++17 for using in C++11
namespace internal { template <class...> using void_t = void; }
///////////////////////////////////////////////////////
// my_def checker
/* primary template handles types that have no nested ::my_def member: */
template <class, class = internal::void_t<>>
struct has_my_def : std::false_type { };
/* specialization recognizes types that do have a nested ::my_def member: */
template <class T>
struct has_my_def<T, internal::void_t<typename T::my_def>> : std::true_type { };
///////////////////////////////////////////////////////
// my_other_def checker
/* primary template handles types that have no nested ::my_other_def member: */
template <class, class = internal::void_t<>>
struct has_my_other_def : std::false_type { };
/* specialization recognizes types that do have a nested ::my_other_def member: */
template <class T>
struct has_my_other_def<T, internal::void_t<typename T::my_other_def>> : std::true_type { };
///////////////////////////////////////////////////////
// freaking sweet: https://stackoverflow.com/a/28253503/3814202
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
template <class T>
static constexpr bool is_valid() {
return all_true< has_my_def<T>::value, has_my_other_def<T>::value >::value;
}
这仅适用于调用函数。这种方法(据我所知)不能用于验证类,因为编译仍将继续。 &#34;技巧&#34;这里只是为函数调用添加了足够的间接层。我无法弄清楚如何通过继承来做到这一点。
///////////////////////////////////////////////////////
// Function example
// does the real work
template <class T>
typename std::enable_if< is_valid<T>(), void>::type
do_actual_work() {
// this never gets compiled from validate_wrapper<Almost_1>
// and therefore never produces compiler warnings about
// `my_other_def` not being a type
using my_def = typename T::my_def;
using my_other_def = typename T::my_other_def;
my_def x = (my_def) 11;
my_other_def y = (my_other_def) 12;
std::cout << "A my_def: " << x << std::endl
<< "A my_other_def: " << y << std::endl;
}
template <class T>
typename std::enable_if< !is_valid<T>(), void>::type
validate() {
// Compilation ends here
static_assert(is_valid<T>(), "You have been asserted.");
}
template <class T>
typename std::enable_if< is_valid<T>(), void>::type
validate() {
std::cout << "Work gets done." << std::endl;
do_actual_work<T>();
}
// user calls this function
template <class T>
void validate_wrapper() {
validate<T>();
}
有关演示此工作的类的定义,请参阅第4节。
static_assert
确实失败了,但是&#34;暴露了&#34; TheClass
(不是internal::TheClass
)将继续尝试using
语句。对于此代码的真实意图,这会产生数百个警告,这就是我尝试这样做的原因。
///////////////////////////////////////////////////////
// Need some more proxying?
namespace internal {
template <class T, bool valid>
struct TheClass {
// compiler warning 1 (seek termination here)
static_assert(valid, "You have been asserted.");
};
template <class T>
struct TheClass<T, true> {
std::string message() { return "You are valid."; }
};
}
template <class T>
struct TheClass : public internal::TheClass<T, is_valid<T>()> {
// compiler warning 2
using my_def = typename T::my_def;
// compiler warning 3
using my_other_def = typename T::my_other_def;
std::string message() {
return internal::TheClass<T, is_valid<T>()>::message();
}
};
///////////////////////////////////////////////////////
// The tests
struct Good {
using my_def = float;
using my_other_def = int;
};
struct Almost_1 { using my_def = double; };
struct Almost_2 { using my_other_def = bool; };
struct Bad {};
int main(int argc, const char **argv) {
// sanity checks
std::cout << std::boolalpha
<< "Good: " << is_valid<Good>() << std::endl
<< "Almost_1: " << is_valid<Almost_1>() << std::endl
<< "Almost_2: " << is_valid<Almost_2>() << std::endl
<< "Bad: " << is_valid<Bad>() << std::endl;
// in function land, uncomment these and it will
// only produce the single static_assert for valid<T>
// rather than continuing on for breaking T
// validate_wrapper<Good>();
// validate_wrapper<Almost_1>();
// At least this works.
TheClass<Good> g;
std::cout << "Good message: " << g.message() << std::endl;
// I would like to achieve just one error from static_assert
// comment out to see that `validate_wrapper<Almost_1>` will
// terminate early (as desired)
TheClass<Almost_1> a1;
return 0;
}
最后,如果您在main
中保留功能版本取消注释,您可以观察到我试图避免的相同行为。
static_assert
的{{1}}。validate_wrapper<Almost_1>
仍然会被编译并产生更多警告。
TheClass<Almost_1>
,则表示您不会收到来自TheClass<Almost_1>
的警告。do_actual_work
目前正在编译,所以它会一直看到它(根据我的理解,这是SFINAE的整点)。所以,由于main
已经开始编译,所以无论基类中的TheClass<Almost_1>
是什么,它都会一直持续到最后。是否有任何推迟继承的聪明方法?我尝试了curiously recurring template pattern思考的一个变体,就是如何做到这一点,但它实际上最终产生了更多的警告。
答案 0 :(得分:0)
希望还有另一种方法,但似乎最简单的解决方案是采用另一种方式:用户应该实例化的类没有实际的实现,internal
类可以完成所有操作。
namespace internal {
template <class T, bool valid>
struct TheClass {
static_assert(valid, "Template 'class T' must have static types `my_def` and `my_other_def`.");
};
template <class T>
struct TheClass<T, true> {
// compiler warning 2
using my_def = typename T::my_def;
// compiler warning 3
using my_other_def = typename T::my_other_def;
std::string message() {
return "work gets done";
}
};
}
template <class T>
struct TheClass : internal::TheClass<T, is_valid<T>()> { };
struct Good {
using my_def = float;
using my_other_def = int;
};
struct Almost_1 { using my_def = double; };
int main(int argc, const char **argv) {
TheClass<Good> g;
std::cout << "Good message: " << g.message() << std::endl;
// only one static assert
TheClass<Almost_1> a1;
return 0;
}