编写一个概念检查库,但在所有基本算术类型上都失败了
write a concept check library, but fail on all fundemental arithmetic types
当我测试像 int 这样的基本算术类型和像 int* 这样的原始指针是否可以是 pre-incrementabl/post-incrementable。
时出了问题
//has_operator_overload_functions.hpp
namespace concept_check {
template <class T>
T create_T();
struct disambiguation_t2 {};
struct disambiguation_t1 {
constexpr operator disambiguation_t2 () const noexcept;
};
}
# define create_has_op_overload_fn_test(fn_name, ret_t_of_op_to_test) \
template <class T> \
auto has_ ## fn_name (disambiguation_t1) -> ret_t_of_op_to_test; \
template <class T> \
void has_ ## fn_name (disambiguation_t2); \
template <class T> \
inline constexpr const bool has_ ## fn_name ## _v = !is_same_v<decltype(has_ ## fn_name <T>(disambiguation_t1{})), void>;
# define create_check_op_overload_fn_return_type_test(fn_name, ret_t_of_op_to_test, result_t) \
create_has_op_overload_fn_test(fn_name, ret_t_of_op_to_test) \
_create_check_op_overload_fn_return_type_test(fn_name, ret_t_of_op_to_test, result_t)
# define _create_check_op_overload_fn_return_type_test(fn_name, ret_t_of_op_to_test, result_t) \
template <class T> \
constexpr const bool is_ ## fn_name () noexcept { \
if constexpr(has_ ## fn_name ## _v<T>) \
return is_convertible_v< ret_t_of_op_to_test , result_t >; \
else \
return 0; \
} \
template <class T> \
inline constexpr const bool is_ ## fn_name ## _v = is_ ## fn_name <T>();
namespace concept_check {
create_check_op_overload_fn_return_type_test(pre_incrementable,
decltype(++create_T<T>()), T&)
create_check_op_overload_fn_return_type_test(post_incrementable,
decltype(create_T<T>()++), T)
}//namespace concept_check
//test.hpp
#include <iostream>
#include "has_operator_overload_functions.hpp"
using namespace concept_check;
struct A {
A& operator ++ ();
A operator ++ (int);
};
struct B {
B& operator ++ ();
};
struct C {
C operator ++ (int);
};
struct E {};
int main() {
std::cout << std::boolalpha
<< "incrementable\n\n"
<< "int\n"
<< is_pre_incrementable_v<int> << std::endl
<< is_post_incrementable_v<int> << std::endl
<< "\nchar\n"
<< is_pre_incrementable_v<char> << std::endl
<< is_post_incrementable_v<char> << std::endl
<< "\nfloat\n"
<< is_pre_incrementable_v<float> << std::endl
<< is_post_incrementable_v<float> << std::endl
<< "\ndouble\n"
<< is_pre_incrementable_v<double> << std::endl
<< is_post_incrementable_v<double> << std::endl
<< "\nchar*\n"
<< is_pre_incrementable_v<char*> << std::endl
<< is_post_incrementable_v<char*> << std::endl
<< "\nvoid*\n"
<< is_pre_incrementable_v<void*> << std::endl
<< is_post_incrementable_v<void*> << std::endl
<< "\nA\n"
<< is_pre_incrementable_v<A> << std::endl
<< is_post_incrementable_v<A> << std::endl
<< "\nB\n"
<< is_pre_incrementable_v<B> << std::endl
<< is_post_incrementable_v<B> << std::endl
<< "\nC\n"
<< is_pre_incrementable_v<C> << std::endl
<< is_post_incrementable_v<C> << std::endl
<< "\nE\n"
<< is_pre_incrementable_v<E> << std::endl
<< is_post_incrementable_v<E> << std::endl
<< "\nbool\n"
<< is_pre_incrementable_v<bool> << std::endl
<< is_post_incrementable_v<bool> << std::endl
}
我用
编译了它
clang version 5.0.1-svn324012-1~exp1 (branches/release_50)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.3.0
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
,使用命令行参数 -std=c++17 -I .. 和 运行 我得到的二进制文件,它给出了以下输出:
incrementable
int
false
false
char
false
false
float
false
false
double
false
false
char*
false
false
void*
false
false
A
true
true
B
true
false
C
false
true
E
false
false
bool
false
false
如您所见,所有基础算术类型和原始指针都没有通过 pre-icrementable/post-incrementable 测试。
任何人都可以解释为什么会发生这种情况以及如何解决这个问题吗?
create_check_op_overload_fn_return_type_test(pre_incrementable, decltype(++create_T<T>()), T&)
您正在使用 decltype(++create_T<T>())
作为对 is_pre_incrementable_v
检查的有效约束。对于 A
这样的类型,这等同于测试,例如++A {}
这是有效的,正如您的结果所反映的那样。
然而,对于 int
这样的类型,这等同于测试 ++0
,这不是一个有效的表达式。 (对于其他非 class 类型,依此类推。)
没有明显的解决方法。您可以决定限制例如++create_T<T&>()
代替(这也是提及 std::declval
的好时机)。但请注意,这会产生以下后果:
template<typename Arg>
auto under_constrained(Arg const& arg)
-> std::enable_if_t<is_pre_incrementable_v<Arg>>
{
++arg;
}
template<typename Arg>
auto over_constrained(Arg& arg)
-> std::enable_if_t<is_pre_incrementable_v<Arg>, std::decay_t<Arg>>
{
auto copy = arg;
++copy;
return copy;
}
- 第一个例子是欠约束的,因为它有效地测试了
++mutable_arg
给定一个虚构的 Arg mutable_arg;
变量,但是正文对一个不可变变量(当然在大多数情况下不会递增)进行操作情况)
- 第二个示例过度约束,因为例如如果调用
int const immutable_variable = 0;
作为参数,它会有效地测试 ++immutable_arg
,但正文在 int
变量 上运行
现在,可以调整这些示例,以便将它们限制在例如is_pre_incrementable_v<Arg const&>
和 is_pre_incrementable_v<std::decay_t<Arg>&>
分别。但这需要概念用户方面的注意。这就是修复不一定是首选解决方案的原因。
对于给定的概念,这一切都归结为 。
当我测试像 int 这样的基本算术类型和像 int* 这样的原始指针是否可以是 pre-incrementabl/post-incrementable。
时出了问题//has_operator_overload_functions.hpp
namespace concept_check {
template <class T>
T create_T();
struct disambiguation_t2 {};
struct disambiguation_t1 {
constexpr operator disambiguation_t2 () const noexcept;
};
}
# define create_has_op_overload_fn_test(fn_name, ret_t_of_op_to_test) \
template <class T> \
auto has_ ## fn_name (disambiguation_t1) -> ret_t_of_op_to_test; \
template <class T> \
void has_ ## fn_name (disambiguation_t2); \
template <class T> \
inline constexpr const bool has_ ## fn_name ## _v = !is_same_v<decltype(has_ ## fn_name <T>(disambiguation_t1{})), void>;
# define create_check_op_overload_fn_return_type_test(fn_name, ret_t_of_op_to_test, result_t) \
create_has_op_overload_fn_test(fn_name, ret_t_of_op_to_test) \
_create_check_op_overload_fn_return_type_test(fn_name, ret_t_of_op_to_test, result_t)
# define _create_check_op_overload_fn_return_type_test(fn_name, ret_t_of_op_to_test, result_t) \
template <class T> \
constexpr const bool is_ ## fn_name () noexcept { \
if constexpr(has_ ## fn_name ## _v<T>) \
return is_convertible_v< ret_t_of_op_to_test , result_t >; \
else \
return 0; \
} \
template <class T> \
inline constexpr const bool is_ ## fn_name ## _v = is_ ## fn_name <T>();
namespace concept_check {
create_check_op_overload_fn_return_type_test(pre_incrementable,
decltype(++create_T<T>()), T&)
create_check_op_overload_fn_return_type_test(post_incrementable,
decltype(create_T<T>()++), T)
}//namespace concept_check
//test.hpp
#include <iostream>
#include "has_operator_overload_functions.hpp"
using namespace concept_check;
struct A {
A& operator ++ ();
A operator ++ (int);
};
struct B {
B& operator ++ ();
};
struct C {
C operator ++ (int);
};
struct E {};
int main() {
std::cout << std::boolalpha
<< "incrementable\n\n"
<< "int\n"
<< is_pre_incrementable_v<int> << std::endl
<< is_post_incrementable_v<int> << std::endl
<< "\nchar\n"
<< is_pre_incrementable_v<char> << std::endl
<< is_post_incrementable_v<char> << std::endl
<< "\nfloat\n"
<< is_pre_incrementable_v<float> << std::endl
<< is_post_incrementable_v<float> << std::endl
<< "\ndouble\n"
<< is_pre_incrementable_v<double> << std::endl
<< is_post_incrementable_v<double> << std::endl
<< "\nchar*\n"
<< is_pre_incrementable_v<char*> << std::endl
<< is_post_incrementable_v<char*> << std::endl
<< "\nvoid*\n"
<< is_pre_incrementable_v<void*> << std::endl
<< is_post_incrementable_v<void*> << std::endl
<< "\nA\n"
<< is_pre_incrementable_v<A> << std::endl
<< is_post_incrementable_v<A> << std::endl
<< "\nB\n"
<< is_pre_incrementable_v<B> << std::endl
<< is_post_incrementable_v<B> << std::endl
<< "\nC\n"
<< is_pre_incrementable_v<C> << std::endl
<< is_post_incrementable_v<C> << std::endl
<< "\nE\n"
<< is_pre_incrementable_v<E> << std::endl
<< is_post_incrementable_v<E> << std::endl
<< "\nbool\n"
<< is_pre_incrementable_v<bool> << std::endl
<< is_post_incrementable_v<bool> << std::endl
}
我用
编译了它clang version 5.0.1-svn324012-1~exp1 (branches/release_50)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.3.0
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
,使用命令行参数 -std=c++17 -I .. 和 运行 我得到的二进制文件,它给出了以下输出:
incrementable
int
false
false
char
false
false
float
false
false
double
false
false
char*
false
false
void*
false
false
A
true
true
B
true
false
C
false
true
E
false
false
bool
false
false
如您所见,所有基础算术类型和原始指针都没有通过 pre-icrementable/post-incrementable 测试。
任何人都可以解释为什么会发生这种情况以及如何解决这个问题吗?
create_check_op_overload_fn_return_type_test(pre_incrementable, decltype(++create_T<T>()), T&)
您正在使用 decltype(++create_T<T>())
作为对 is_pre_incrementable_v
检查的有效约束。对于 A
这样的类型,这等同于测试,例如++A {}
这是有效的,正如您的结果所反映的那样。
然而,对于 int
这样的类型,这等同于测试 ++0
,这不是一个有效的表达式。 (对于其他非 class 类型,依此类推。)
没有明显的解决方法。您可以决定限制例如++create_T<T&>()
代替(这也是提及 std::declval
的好时机)。但请注意,这会产生以下后果:
template<typename Arg>
auto under_constrained(Arg const& arg)
-> std::enable_if_t<is_pre_incrementable_v<Arg>>
{
++arg;
}
template<typename Arg>
auto over_constrained(Arg& arg)
-> std::enable_if_t<is_pre_incrementable_v<Arg>, std::decay_t<Arg>>
{
auto copy = arg;
++copy;
return copy;
}
- 第一个例子是欠约束的,因为它有效地测试了
++mutable_arg
给定一个虚构的Arg mutable_arg;
变量,但是正文对一个不可变变量(当然在大多数情况下不会递增)进行操作情况) - 第二个示例过度约束,因为例如如果调用
int const immutable_variable = 0;
作为参数,它会有效地测试++immutable_arg
,但正文在int
变量 上运行
现在,可以调整这些示例,以便将它们限制在例如is_pre_incrementable_v<Arg const&>
和 is_pre_incrementable_v<std::decay_t<Arg>&>
分别。但这需要概念用户方面的注意。这就是修复不一定是首选解决方案的原因。
对于给定的概念,这一切都归结为