C++ 模板第二版- 完整指南在第 435 页包含以下代码
#include <string>
#include <type_traits>
template<typename T, typename = void>
struct HasBeginT : std::false_type {};
template<typename T>
struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>>
: std::true_type {};
Run Code Online (Sandbox Code Playgroud)
和用于测试调用 adecltype(std::declval<T>().begin())
是否有效的注释。.begin()
T
这一切都有道理,我想……
令我震惊的是脚注中的评论:
除此之外,与其他上下文中的调用表达式不同,它不需要非引用、非返回类型来完成。使用相反,确实增加了调用返回类型完整的要求,因为返回值不再是操作数的结果。
decltype(call-expression)
void
decltype(std::declval<T>().begin(), 0)
decltype
我不太明白。
为了尝试使用它,我尝试使用以下代码查看它对void
member的行为begin
struct A {
void begin() const;
};
struct B {
};
static_assert(HasBeginT<A>::value, "");
static_assert(!HasBeginT<B>::value, "");
Run Code Online (Sandbox Code Playgroud)
但无论有没有 . ,这两个断言都通过了, 0
。
我最近在clang和gcc之间遇到了关于void_t
属性检测和受保护/私有类信息的一些不同行为.考虑一个类型特征,定义如下:
#include <type_traits>
template<typename T, typename = void>
constexpr const bool has_nested_type_v = false;
template<typename T>
constexpr const bool has_nested_type_v
<T, std::void_t<typename T::type>> = true;
Run Code Online (Sandbox Code Playgroud)
给定具有受保护或私有嵌套type
类和简单程序的示例类型
#include "has_nested_type.hpp"
#include <iostream>
struct Protected {
protected:
struct type{};
};
struct Private {
private:
struct type{};
};
int main() {
std::cout << "Protected: "
<< (has_nested_type_v<Protected> ? "detected" : "not detected")
<< std::endl;
std::cout << "Private: "
<< (has_nested_type_v<Private> ? "detected" : "not detected")
<< std::endl;
}
Run Code Online (Sandbox Code Playgroud)
clang成功编译失败的检测(如预期的那样).该程序,编译和输出上再现wandbox 这里 …
使用SFINAE,我需要检测容器的某些属性。
例如:
#include <type_traits>
#include <vector>
template <typename Container, typename = void>
struct Trait {};
template <typename Container>
struct Trait<
Container,
std::enable_if_t<std::is_integral_v<typename Container::value_type>>> {
static constexpr bool foo = false;
};
template <typename Container>
struct Trait<
Container,
std::enable_if_t<std::is_floating_point_v<typename Container::value_type>>> {
static constexpr bool foo = true;
};
static_assert(Trait<std::vector<int>>::foo == false);
static_assert(Trait<std::vector<double>>::foo == true);
Run Code Online (Sandbox Code Playgroud)
上面的代码可以按预期进行编译和运行(在clang和gcc上)。在这里检查。
但是,如果我稍加修改将模板类型包装在里面的代码std::void_t
:
template <typename Container>
struct Trait<
Container,
std::void_t<std::enable_if_t<std::is_integral_v<typename Container::value_type>>>> {
static constexpr bool foo …
Run Code Online (Sandbox Code Playgroud) C++20 已经登陆,带来了概念。如果一个项目现在开始并且仅针对 C++20 和更高版本的标准,那么说以前的约束形式现在被概念和子句取代是否合适requires
?
是否存在某些情况下enable_if
或void_t
仍然需要,而概念无法替代它们?
例如,std::bit_cast
被限制为要求To
和From
类型具有相同的大小,并且两种类型都可以轻松复制。
来自 cppreference:
仅当 sizeof(To) == sizeof(From) 并且 To 和 From 都是 TriviallyCopyable 类型时,此重载才参与重载决策。
MSVC 标准库通过enable_if_t
表达式来限制这一点,而 libcxx 选择子句requires
。
微软VC:
enable_if_t<conjunction_v<bool_constant<sizeof(_To) == sizeof(_From)>, is_trivially_copyable<_To>, is_trivially_copyable<_From>>, int> = 0
Run Code Online (Sandbox Code Playgroud)
libcxx:
requires(sizeof(_ToType) == sizeof(_FromType) &&
is_trivially_copyable_v<_ToType> &&
is_trivially_copyable_v<_FromType>)
Run Code Online (Sandbox Code Playgroud)
它们通过不同的语言特征执行相同的逻辑运算。
重申我的问题,是否存在无法从一种约束方法转换为另一种约束方法的情况?我知道有很多东西概念requires
可以做到enable_if
和/或void_t
不能做到,但是从另一个方向看也可以这么说吗?针对 C++20 及更高版本的全新代码库是否需要依赖这些旧的语言结构?
请考虑以下代码:
template <class F, class... Args, class = std::void_t<>>
struct is_invokable
: std::false_type {};
template <class F, class... Args>
struct is_invokable<F, Args..., std::void_t<std::invoke_result_t<F, Args...>>>
: std::true_type {};
Run Code Online (Sandbox Code Playgroud)
目标是有一个特性,它能够判断类型F
的可调参数是否可以使用类型的参数调用Args...
.
但是,它无法编译,因为:
error: parameter pack 'Args' must be at the end of the template parameter list
Run Code Online (Sandbox Code Playgroud)
在C++ 17中执行此操作的(优雅)方式是什么?
在 中C++17
,void_t
允许使用class
/struct
模板轻松执行 SFINAE:
template <class T, class = void>
struct test {
static constexpr auto text = "general case";
};
template <class T>
struct test<T, std::void_t<decltype(std::begin(std::declval<T>())>> {
static constexpr auto text = "has begin iterator";
};
Run Code Online (Sandbox Code Playgroud)
里面的东西void_t
是一个类型。void_t
我的问题是:当里面的内容是类型特征时,如何做同样的事情。使用enable_if
效果很好:
template <class T>
struct test<T, std::void_t<std::enable_if_t<std::is_class_v<T>>> {
static constexpr auto text = "is a class";
};
Run Code Online (Sandbox Code Playgroud)
是否有更短/更优雅的方式来写这个,或者“正确的方式”来做到这一点,真的是结合void_t
and enable_if
?
这一切看起来很简单。
在我下面的代码中,对于 T=vector 来说,has_member_type::value 不应该是真的吗?对不起,如果它已经得到回答。我找了一会儿,但找不到答案。
#include <iostream>
#include <type_traits>
// has_value_type
template <typename, typename = std::void_t<>>
struct has_value_type: std::false_type {};
template <typename T>
struct has_value_type<T, std::void_t<typename T::value_type>>: std::true_type {};
int main()
{
std::cout << "bool: " << std::boolalpha << has_value_type<bool>::value << std::endl;
std::cout << "vector_has: " << std::boolalpha << has_value_type<std::vector<int>>::value << std::endl;
std::cout << "true_type: " << std::boolalpha << std::true_type::value << std::endl;
std::cout << "false_type: " << std::boolalpha << std::false_type::value << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
bool: false
vector_has: false …
Run Code Online (Sandbox Code Playgroud) 在void_t
尝试检测习惯用法时,我发现涉及减法运算符和 void* 的表达式会导致测试编译器中出现不同的错误。
GCC 10.2 及以下版本,无论使用 C++11、14、17 还是 20,似乎都将某些表达式视为硬错误而不是替换失败。
我正在使用以下(精简)示例
#include <type_traits>
template< class, class, class = void >
struct has_minus_void_t : std::false_type {};
template<class T, class U>
struct has_minus_void_t<T, U, std::void_t<
decltype(T() - U())
>> : std::true_type {};
static_assert(has_minus_void_t<int*,int*>::value, "int*");
static_assert(!has_minus_void_t<int*,void*>::value, "intvoid*");
static_assert(!has_minus_void_t<void*,int*>::value, "voidint*");
static_assert(!has_minus_void_t<void*,void*>::value, "void*");
Run Code Online (Sandbox Code Playgroud)
并且希望代码能够针对所有四种语言标准进行编译而不会出错(包含std::void_t
C++17 之前的替代品,因此可以测试更多标准)但是
我试图搜索现有的错误报告,但找不到。
我想知道哪个编译器是正确的,为什么。
我正在尝试void_t
使用用法,但是以下代码给出了编译错误。is_fun
是CompS
struct 中的typedef,所以我认为Comp::is_fun
应该是有效的。
我有什么想念的吗?
template <typename T, typename Comp, typename = void_t<>>
class my_set
{
public:
my_set() : mem(5){}
T mem;
};
template <typename T, typename Comp, void_t<typename Comp::is_fun> >
class my_set
{
public:
my_set() : mem(10){}
T mem;
};
struct CompS
{
typedef int is_fun;
};
int main()
{
my_set<int, CompS> a;
std::cout << a.mem << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
错误:
voidt.cpp:17:38: error: ‘void’ is not a valid type for a template …
Run Code Online (Sandbox Code Playgroud)