我正在一个新项目中使用 Concepts TS。我的问题与结构模板和我想要创建的关联概念之间的看似循环依赖有关。该概念的具体逻辑是检查该概念的类型参数是否是结构模板的特化。由于我希望该概念可在结构模板内部使用,因此我显然需要在结构模板之前定义该概念,但概念的逻辑也需要了解结构模板。我已经通过前向声明结构模板Vector,然后定义概念VectorSpecialization,最后定义结构模板,找到了可以编译的东西Vector。requires我的具体问题与我使用结构模板的子句有关;当我转发声明它时,编译器会给我一个错误,除非我复制完整的requires子句。(参见下面的代码)。
我的具体问题是:有没有办法避免requires模板的前向声明和定义之间的子句完全重复?一种可能性是将子句的逻辑分解requires为声明和定义都可以委托给的公共实体,我认为这将解决 DRY 原则;但我很好奇我是否可以在这里做出更高级别的结构决策,以避免requires在两个地方都需要一个子句,或者是否有一种更惯用的方式在这样的用例中使用概念,我可以从中受益从。重申一下,我所说的用例是:编写一个将在模板中使用的概念,但该概念还需要了解模板。
// Forward declare the struct template so that the concept can refer to it
// Note the need to repeat the 'requires' clause. Can that repetition be
// be avoided?
template< typename T, size_t N > requires N > 1 struct Vector;
// compile-time overload set using template arg deduction to detect
// …Run Code Online (Sandbox Code Playgroud) 新发布的草案在[expr.prim.req]/6 中提到:
如果将模板参数替换为需求总是会导致替换失败,则程序格式错误;无需诊断。[ 示例:
Run Code Online (Sandbox Code Playgroud)template<typename T> concept C = requires { new int[-(int)sizeof(T)]; // ill-formed, no diagnostic required };— 结束示例 ]
但是为什么我们不能保证诊断总是失败,而不是跳过诊断?
我偶然发现了这个:
#include <type_traits>
#include <concepts>
template<class T>
concept IsFoo = requires(T a)
{
{a.a} -> std::same_as<int>;
};
#if 1
// Will not compile, because Foo currently has incomplete type
template<IsFoo AFoo>
struct AcceptsFoo
{};
#else
template<class AFoo>
struct AcceptsFoo
{};
#endif
struct Foo
{
int a;
int b;
AcceptsFoo<Foo> obj;
};
Run Code Online (Sandbox Code Playgroud)
https://gcc.godbolt.org/z/j43s4z
其他变体 (crtp) https://gcc.godbolt.org/z/GoWfhq
Foo 是不完整的,因为它必须实例化AcceptsFoo,但要这样做,Foo必须是完整的,否则无法检查IsFoo。这是 GCC 中的错误,还是标准这么说?后者会令人难过,因为这会阻止将概念与一些众所周知的模式(例如 CRTP)一起使用。
我注意到 clang 确实给出了类似的错误:https : //gcc.godbolt.org/z/d5bEez
我正在尝试std::iter_value_t使用 C++20 概念实现递归版本,以便可以检索T类似嵌套容器的基本类型std::vector<std::vector<...std::vector<T>...>>。实验实现如下。
template<typename T>
concept is_iterable = requires(T x)
{
*std::begin(x);
std::end(x);
};
template<typename T> requires (!is_iterable<T>)
struct recursive_iter_value_t_detail
{
typedef typename T type;
};
template<typename T> requires (is_iterable<T>)
struct recursive_iter_value_t_detail
{
typedef typename std::iter_value_t<typename recursive_iter_value_t_detail<T>::type> type;
};
template<typename T>
using recursive_iter_value_t = typename recursive_iter_value_t_detail<T>::type;
Run Code Online (Sandbox Code Playgroud)
尝试编译此代码后,'recursive_iter_value_t_detail': requires clause is incompatible with the declaration弹出了唯一的错误消息,我不确定是什么requires clause is incompatible with the declaration意思。是不是template struct 不能像这样重载的问题?请帮我解决这个问题。
的预期输出recursive_iter_value_t<std::vector<std::vector<int>>>为int。
我想测试 c++20 中的新概念功能,我想知道是否可以创建一个概念来检查声明为 const 的函数是否存在。
如果函数以正确的类型存在但不是 const,我希望检查失败。我在这里找不到任何相关内容:https : //en.cppreference.com/w/cpp/concepts
我有这个
template <typename T>
concept hasToString = requires (T val) {
{ val.toString() } /* const here gives error */ -> std::same_as<std::string>;
};
void f(hasToString auto bar)
{
std::cout << bar.toString();
}
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用概念来检测给定类型的开始函数是否将迭代器返回到“const T&”或仅返回到“T&”。但我不确定如何最好地去做,我尝试了以下方法:
#include <iostream>
#include <concepts>
#include <vector>
#include <set>
template <typename T>
concept IsConst = std::is_const<T>::value;
template<typename T>
concept IsIterableOfConst = requires (T& t) { { *t.begin() } -> IsConst; };
template <IsIterableOfConst T>
void test()
{
std::cout << "Yes" << std::endl;
}
template <typename T>
void test()
{
std::cout << "No" << std::endl;
}
int main(int argc, char** argv)
{
test<std::set<int>>();
test<std::vector<int>>();
}
Run Code Online (Sandbox Code Playgroud)
这会产生输出“No No”,当我希望它产生输出“Yes No”时,因为 std::set 中的迭代器应该,据我所知,总是迭代持有类型的集合的“const”版本. 如何正确检测容器包含 const 值?
给定一个这样的类模板:
template<typename T>
struct foo
{
T data;
};
Run Code Online (Sandbox Code Playgroud)
如何确定是否T是智能指针,例如std::shared_ptr<T_underlying>使用 C++20 概念?
我想foo<T>根据这个标准添加功能。例如,我想使用新概念系统,而不是使用 SFINAE。
我想实现这样的目标:
template<typename T>
struct foo
{
T data;
void func()
requires is_shared_ptr_v<T>
{
// ...
}
};
Run Code Online (Sandbox Code Playgroud)
STL 中是否存在相关概念?如果没有,我假设我可以写一个概念 for std::shared_ptr, one forstd::unique_ptr等等,然后将它们与逻辑或一般is_smart_pointer概念联系在一起?
考虑以下程序:
#include <iostream>
template<typename T> void f1(T& v)
{
std::cout << "f1: can call g" << std::endl;
v.g();
}
template<typename T> void f2(T& v) requires requires (T& v) { v.g(); }
{
std::cout << "f2: can call g" << std::endl;
v.g();
}
template<typename T> void f2(T&) requires (!requires (T& v) { v.g(); })
{
std::cout << "f2: cannot call g" << std::endl;
}
class A
{
public: // if commented out, f2 will not call g anymore
void g()
{ …Run Code Online (Sandbox Code Playgroud) 此问题演示了如何使用 C++20 概念为函数模板选择重载。我正在尝试做一些类似的事情:为类模板选择专业化。
我从一个类模板开始,Angle<T>它包含一个包含弧度角的浮点值。使用概念,我可以确保用户不会Angle使用浮点类型以外的任何东西进行实例化:
template <std::floating_point T> struct Angle { T m_radians; };
Run Code Online (Sandbox Code Playgroud)
后来,我决定让客户使用Angle<T>可以处理整数类型的独特实现。换句话说,我想允许这样的代码:
const auto theta = Angle<float>(3.14f);
const auto phi = Angle<int>(180);
Run Code Online (Sandbox Code Playgroud)
所以我尝试添加一个可比较的模板。
template <std::integral T> struct Angle { T m_degrees; };
Run Code Online (Sandbox Code Playgroud)
编译器将此附加实现视为具有不同约束的模板的重新声明。我尝试了几种不同的方式来表达我的意图,但没有一种方法能满足我尝试过的任何编译器。事实上,我什至找不到用std::enable_if传统 SFINAE来做到这一点的方法——不可否认,我完全有可能不太了解 SFINAE。
我发现的唯一方法需要对每个整数和浮点类型进行不同的专门化。
template <std::floating_point T> struct AngleRad { T m_radians; };
template <std::integral T> struct AngleDeg { T m_degrees; };
template <typename T> struct Angle2 {};
template …Run Code Online (Sandbox Code Playgroud) C++20 的新特性之一是Down with typename。
在 C++17 中,您必须在几乎所有†依赖上下文中提供 typename 关键字以消除类型与值的歧义。但是在 C++20 中,这个规则放宽了很多。在需要类型的所有上下文中, typename 关键字不再是必需的。
template<typename T>
concept IsOK = true;
template<typename T>
requires IsOK<T::U> // error: use ‘typename T::U’
void f()
{}
struct A
{
using U = int;
};
int main()
{
f<A>();
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,显然,IsOK概念只能接受类型。
为什么 typename 这里需要?
看在线演示
c++ ×10
c++-concepts ×10
c++20 ×9
templates ×2
constraints ×1
recursion ×1
require ×1
standards ×1
typename ×1