简体   繁体   中英

C++ Concepts check if variadic types equal a given type(s)

Using C++ concepts, I'd like to be able to define concepts that force all the types in a parameter pack to either be a given type, or in a list of given types. The 4 concepts I've defined are:

  • type_is : check if type T matches type U
  • type_in : check if type T is one of the types in U...
  • all_types_are : check if all types in parameter pack T... match type U
  • all_types_in : check if all types in parameter pack T... are any of the types in U... (can be different types in U... )

The code, with a method for each concept to test agsinst:

#include <concepts>
#include <iostream>
#include <type_traits>


class A{public: int var = 100;};
class B{public: int var = 200;};
class C{public: int var = 300;};


template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = requires
{
    requires std::same_as<std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;
};


template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = requires
{
    requires (std::same_as<std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);
};


template <typename ...TypesToCheck, typename TypeToCheckAgainst>
concept all_types_are = requires
{
    requires (std::same_as<std::remove_cvref_t<TypeToCheckAgainst>, TypesToCheck> && ...);
};


template <typename ...TypesToCheck, typename ...TypesToCheckAgainst>
concept all_types_in = requires
{
    requires (std::same_as<std::remove_cvref_t<TypesToCheck>, TypesToCheckAgainst> || ...);
};


auto method1(type_is<A> auto&& object)
{
    std::cout << object.var;
    std::cout << std::endl;
}


auto method2(type_in<A, B> auto&& object)
{
    std::cout << object.var;
    std::cout << std::endl;
}


auto method3(all_types_are<A> auto&&... objects)
{
    (std::cout << ... << objects.var);
    std::cout << std::endl;
}


auto method4(all_types_in<A, B> auto&&... objects)
{
    (std::cout << ... << objects.var);
    std::cout << std::endl;
}


int main()
{
    A a;
    B b;
    C c;

    method1(a);
    method2(b);
    method3(a, a, a);
    method4(a, b, b);

    return 0;
}

The first 2 concepts that check for a single type T work fine, but for the last 2 concepts, if I call methods constrained with those concepts, with arguments that should conform to the concepts, they still fail with:

main.cpp(75): error C2672: 'method3': no matching overloaded function found
main.cpp(75): error C7602: 'method3': the associated constraints are not satisfied
main.cpp(53): note: see declaration of 'method3'
main.cpp(76): error C2672: 'method4': no matching overloaded function found
main.cpp(76): error C7602: 'method4': the associated constraints are not satisfied
main.cpp(60): note: see declaration of 'method4'

I'm not sure how to correct my last two concepts so that they behave as desired, as I don't understand how the constraints aren't satisfied. What do I need to change in order to make the concepts work?

First, there is no need to use nested requires , just simple do

template <typename TypeToCheck, typename TypeToCheckAgainst>
concept type_is = std::same_as<
  std::remove_cvref_t<TypeToCheck>, TypeToCheckAgainst>;

template <typename TypeToCheck, typename ...TypesToCheckAgainst>
concept type_in = (std::same_as<
  std::remove_cvref_t<TypeToCheck>, TypesToCheckAgainst> || ...);

Second, you don't need to define all_types_are additionally, just reuse type_is / type_in for the variadic template version

auto method3(type_is<A> auto&&... objects) {
  (std::cout << ... << objects.var);
  std::cout << std::endl;
}


auto method4(type_in<A, B> auto&&... objects) {
  (std::cout << ... << objects.var);
  std::cout << std::endl;
}

Demo

Your interpretation of the syntax in

auto method3(all_types_are<A> auto&&... objects)

is wrong. Using a type constraint in a template parameter pack doesn't result in the constraint being checked once for the whole pack expansion.

Instead the constraint will be checked for each element of the pack individually. So all you need is

auto method3(type_is<A> auto&&... objects)

to achieve what you intent all_types_are to do.


It is not possible to use a type constraint to constraint multiple types at once. If you need this (which is not the case here) you will need to use a requires clause instead, eg:

auto method4(auto&&... objects)
requires all_types_in<decltype(objects)..., A, B>
{ /*...*/ }

However, this will fail here as well, because there is no way to decide which of the template arguments in all_types_in<decltype(objects)..., A, B> are supposed to be part of TypesToCheck and which are supposed to be part of TypesToCheckAgainst .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM