C++ 非模板函数上的约束表达式有什么意义?

C++ 非模板函数上的约束表达式有什么意义?,c++,language-lawyer,c++20,C++,Language Lawyer,C++20,[temp.constr.decl]说我们可以用约束表达式约束模板或函数 Declarators[dcl.decl]告诉我们,对于函数,我们可以添加一个可选的尾部requires子句来约束它,该标准甚至给出了以下(似乎毫无意义的)示例: void f1(int a) requires true; auto f2(int a) -> bool requires true; 我理解约束模板或概念是有用的,但我看不出这些约束对非模板函数有何用处约束非模板函数的意义是什么?< P>就像一个概念考

[temp.constr.decl]说我们可以用约束表达式约束模板或函数

Declarators[dcl.decl]告诉我们,对于函数,我们可以添加一个可选的尾部requires子句来约束它,该标准甚至给出了以下(似乎毫无意义的)示例:

void f1(int a) requires true;
auto f2(int a) -> bool requires true;

我理解约束模板或概念是有用的,但我看不出这些约束对非模板函数有何用处<强>约束非模板函数的意义是什么?

< P>就像一个概念考虑下面的例子

#include <iostream>

void f( long x ) requires ( sizeof( long ) == sizeof( int ) )
{
    std::cout << "Bye " << x << '\n';
}

void f( long long x ) requires ( sizeof( long ) == sizeof( long long ) )
{
    std::cout << "Hello " << x << '\n';
}

int main() 
{
    f( 0l );
}
否则

Bye 0
例如,您可以在计算阶乘的函数中使用这种方法来限制循环迭代次数或引发异常

这是一个演示程序

#include <iostream>
#include <stdexcept>

unsigned long factorial( unsigned long n ) noexcept( false ) 
    requires ( sizeof( unsigned long ) == sizeof( unsigned int ) )
{
    const unsigned long MAX_STEPS = 12;

    if ( MAX_STEPS < n ) throw std::out_of_range( "Too big value." );

    unsigned long f = 1;

    for ( unsigned long i = 1; i < n; i++ ) f *= ( i + 1 );

    return f;
}

unsigned long long factorial( unsigned long long n ) noexcept( false ) 
    requires ( sizeof( unsigned long ) == sizeof( unsigned long long ) )
{
    const unsigned long long MAX_STEPS = 20;

    if ( MAX_STEPS < n ) throw std::out_of_range( "Too big value." );

    unsigned long f = 1;

    for ( unsigned long long i = 1; i < n; i++ ) f *= ( i + 1 );

    return f;
}

int main() 
{
    unsigned long n = 20;

    try
    {
        std::cout << factorial( n ) << '\n';
    }
    catch ( const std::out_of_range &ex )
    {
        std::cout << ex.what() << '\n';
    }
}


约束非模板函数的要点之一是能够将约束写入模板类的非模板成员。例如,您可能有如下类型:

template<typename T>
class value
{
public:
  value(const T& t);
  value(T&& t);

private:
  T t_;
};
没有黑客。不将模板应用于不需要作为模板的函数。它只是工作,用户很容易理解正在发生的事情


这对于不能作为模板的函数尤其重要。为了将构造函数视为复制或移动构造函数,它不能是模板。复制/移动赋值运算符也是如此。但是这样的事情可能会有限制。

非常好。现在,我们可以将静态断言从函数体中提升出来,并将其作为签名的一部分。事实上,如果constexpr我希望我可以接受这两个答案,因为它们的质量如此之高,但我觉得Nicolas的答案更符合该功能的用途。很抱歉,你只能接受一次升级投票。@jondoe如果你的编译器没有编译它,并不意味着代码无效。这只意味着你还不明白这一点,什么都不懂。@jondoe你需要使用一个支持require子句的更现代的编译器。
2432902008176640000
Too big value.
template<typename T>
class value
{
public:
  value(const T& t);
  value(T&& t);

private:
  T t_;
};
template<typename T>
class value
{
public:
  value(const T& t) requires is_copy_constructible_v<T> : t_(t) {}
  value(T&& t) requires is_move_constructible_v<T> : t_(std::move(t)) {}

private:
  T t_;
};