• Class / Function / Type

      std::
    • Header file

      <>
    • Other / All

    最終更新日時(UTC):
    が更新

    履歴 編集

    concept
    <concepts>

    std::invocable

    namespace std {
      template<class F, class... Args>
      concept invocable = requires(F&& f, Args&&... args) {
        invoke(std::forward<F>(f), std::forward<Args>(args)...);
      };
    
      template<class F, class... Args>
      concept regular_invocable = invocable<F, Args...>;
    }
    

    概要

    invocable及びregular_invocableは、任意の関数呼び出し可能な型Fが引数Args...によって関数呼び出し可能であることを表すコンセプトである。

    等しさの保持

    invocableコンセプトではFArgs...による関数呼び出しが等しさを保持することを要求しない。従って、invocableコンセプトのモデルとなる型F, Args...は関数呼び出しに際して副作用があっても良く、その出力が内部状態や外部状態に依存していても構わない。

    対して、regular_invocableコンセプトのモデルとなるFArgs...による関数呼び出しには等しさを保持することが要求される。従って、regular_invocableコンセプトのモデルとなる型F, Args...は関数呼び出しに際して副作用を持ってはならず、出力は何かしらの状態に依存してはならない。ただし、このことは構文的に(コンパイル時に)チェックされるものではなく、純粋に意味論的な制約として要求・表明される。

    例えば、乱数・分布生成器はその呼び出しに際して等しさを保持しない(内部に状態を保ち、出力はそれに依存する)ため、regular_invocableコンセプトのモデルにはならないがinvocableコンセプトのモデルとなる。

    invocable

    #include <iostream>
    #include <concepts>
    #include <random>
    
    template<typename F, typename... Args>
    requires std::invocable<F, Args...>
    void f(const char* name) {
      std::cout << name << " is invocable" << std::endl;
    }
    
    template<typename F, typename... Args>
    void f(const char* name) {
      std::cout << name << " is not invocable" << std::endl;
    }
    
    
    void func(int);
    auto lambda = [](auto a) { return a; };
    auto mut_lambda = [n = 0](auto a) mutable { ++n; return n + a; };
    
    struct invocable {
      template<typename T>
      void operator()(T&& t) const {
        return t;
      }
    };
    
    struct not_invocable {};
    
    
    int main() {
      f<decltype(func), int>("func(int)");
      f<decltype(lambda), int>("lambda(int)");
      f<decltype(lambda), int*>("lambda(int*)");
      f<invocable, int>("invocable(int)");
      f<invocable, int***>("invocable(int***)");
    
      // 内部状態を保ち、等しさを保持しない呼び出し可能な型
      f<decltype(mut_lambda), int>("mut_lambda(int)");
      f<std::mt19937>("std::mt19937()");
    
      std::cout << "\n";
    
      f<decltype(func), int*>("func(int*)");
      f<not_invocable>("not_invocable()");
      f<not_invocable, int>("not_invocable(int)");
    }
    

    出力

    func(int) is invocable
    lambda(int) is invocable
    lambda(int*) is invocable
    invocable(int) is invocable
    invocable(int***) is invocable
    
    mut_lambda(int) is invocable
    std::mt19937() is invocable
    
    func(int*) is not invocable
    not_invocable() is not invocable
    not_invocable(int) is not invocable
    

    regular_invocable

    #include <iostream>
    #include <concepts>
    #include <random>
    
    template<typename F, typename... Args>
    requires std::regular_invocable<F, Args...>
    void f(const char* name) {
      std::cout << name << " is regular_invocable" << std::endl;
    }
    
    template<typename F, typename... Args>
    void f(const char* name) {
      std::cout << name << " is not regular_invocable" << std::endl;
    }
    
    
    void func(int);
    auto lambda = [](auto a) { return a; };
    auto mut_lambda = [n = 0](auto a) mutable { ++n; return n + a; };
    
    struct invocable {
      template<typename T>
      void operator()(T&& t) const {
        return t;
      }
    };
    
    struct not_invocable {};
    
    
    int main() {
      f<decltype(func), int>("func(int)");
      f<decltype(lambda), int>("lambda(int)");
      f<decltype(lambda), int*>("lambda(int*)");
      f<invocable, int>("invocable(int)");
      f<invocable, int***>("invocable(int***)");
    
      std::cout << "\n";
      // 内部状態を保ち、等しさを保持しない呼び出し可能な型
      f<decltype(mut_lambda), int>("mut_lambda(int)");
      f<std::mt19937>("std::mt19937()");
      // これらの型は std::regular_invocable コンセプトのモデルではないが
      // C++構文上では std::invocable との差異を区別しない/できないため
      // それぞれ「XXX is regular_invocable」と出力される。
    
      std::cout << "\n";
      f<decltype(func), int*>("func(int*)");
      f<not_invocable>("not_invocable()");
      f<not_invocable, int>("not_invocable(int)");
    }
    

    出力

    func(int) is regular_invocable
    lambda(int) is regular_invocable
    lambda(int*) is regular_invocable
    invocable(int) is regular_invocable
    invocable(int***) is regular_invocable
    
    mut_lambda(int) is regular_invocable
    std::mt19937() is regular_invocable
    
    func(int*) is not regular_invocable
    not_invocable() is not regular_invocable
    not_invocable(int) is not regular_invocable
    

    バージョン

    言語

    • C++20

    処理系

    関連項目

    参照