简体   繁体   English

c++20 概念:如何使用可能存在或不存在的类型?

[英]c++20 concepts: How can I use a type that may or may not exist?

I have begun a project that makes heavy use of c++20 concepts as a way of learning some of the new c++20 features.我已经开始了一个项目,该项目大量使用 c++20 概念作为学习一些新的 c++20 功能的一种方式。 As part of it, I have a function template that takes a single argument and operates on it.作为其中的一部分,我有一个函数模板,它接受一个参数并对其进行操作。 I wish to have the flexibility to pass types to this function that specify another type that they operate on, but that defaults to something else if that specification doesn't exist.我希望能够灵活地将类型传递给此函数,以指定它们操作的另一种类型,但如果该规范不存在,则默认为其他类型。

For example, here is a type that specifies a type that it operates on:例如,这里有一个类型,它指定了它所操作的类型:

struct has_typedef_t
{
    typedef int my_type; //operates on this type
    void use_data(const my_type& data) const
    {
         //do something else
    }
};

and one that does not specify a type, but operates on a default type specified elsewhere:以及一个没有指定类型,但在别处指定的默认类型上运行的类型:

typedef std::string default_type;
struct has_no_typedef_t
{
    void use_data(const default_type& data) const
    {
        //do something
    }
};

I have a simple concept that can tell me if any given type has this specification:我有一个简单的概念可以告诉我任何给定类型是否具有此规范:

template <class T> concept has_type = requires(T t) {typename T::my_type;};

And the function might look something like the following:该函数可能如下所示:

template <class T> void my_function(const T& t)
{
    //Here I want a default-constructed value of the default
    //type if the argument doesn't have a typedef
    typename std::conditional<has_type<T>, typename T::my_type, default_type>::type input_data;
    t.use_data(input_data);
}

The problem here is that the second template argument is invalid for anything that doesn't specify a type, eg has_no_typedef_t .这里的问题是第二个模板参数对于没有指定类型的任何东西都是无效的,例如has_no_typedef_t An example program:一个示例程序:

int main(int argc, char** argv)
{
    has_no_typedef_t s1;
    has_typedef_t    s2;
    
    // my_function(s1); // doesn't compile:
    //'has_no_typedef_t has no type named 'my_type'
    
    my_function(s2); //compiles
    return 0;
}

What I am looking for is a replacement for the following line:我正在寻找的是以下行的替代品:

typename std::conditional<has_type<T>, typename T::my_type, default_type>::type input_data;

as this is causing the problem.因为这是造成问题的原因。 I am aware that I can pull of some tricks using an overloaded function using the concept above and combining decltype and declval, but I am looking for something clean and have not been able to come up with anything.我知道我可以使用上面的概念并结合 decltype 和 declval 使用重载函数来获得一些技巧,但是我正在寻找一些干净的东西并且无法提出任何东西。 How can I achieve this behavior cleanly?我怎样才能干净地实现这种行为?

Below is the full code for the full picture (c++20):下面是全图的完整代码(c++20):

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

template <class T> concept has_type = requires(T t) {typename T::my_type;};

struct has_typedef_t
{
    typedef int my_type;
    void use_data(const my_type& data) const
    {
        //do something
    }
};

typedef std::string default_type;
struct has_no_typedef_t
{
    void use_data(const default_type& data) const
    {
        //do something else
    }
};



template <class T> void my_function(const T& t)
{
    //Here I want a default-constructed value of the default
    //type if the argument doesn't have a typedef
    typename std::conditional<has_type<T>, typename T::my_type, default_type>::type input_data;
    t.use_data(input_data);
}

int main(int argc, char** argv)
{
    has_no_typedef_t s1;
    has_typedef_t    s2;
    
    // my_function(s1); // doesn't compile:
    //'has_no_typedef_t has no type named 'my_type'

    my_function(s2); //compiles
    return 0;
}

This has had a solution since C++98: a traits class.自 C++98 以来,这已经有了一个解决方案:一个特征类。 Concepts just makes it a bit easier to implement:概念只是让它更容易实现:

template<typename T>
struct traits
{
  using type = default_type;
};

template<has_type T>
struct traits<T>
{
  using type = T::my_type;
};

Without concepts, you'd need to use SFINAE to turn on/off the specializations based on whether the type has the trait or not.如果没有概念,您需要使用 SFINAE 根据类型是否具有特征来打开/关闭专业化。

You can use lambda combined with if constexpr to determine the type of the return.您可以将 lambda 与if constexpr结合使用来确定返回的类型。 The type_identity here is to solve the problem that the type may not be default_initializable .这里的type_identity是为了解决类型可能不是default_initializable的问题。

typename decltype([]{
  if constexpr (requires { typename T::my_type; }) 
    return std::type_identity<typename T::my_type>{};
  else
    return std::type_identity<default_type>{};
}())::type input_data;

t.use_data(input_data);

Demo演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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