Есть ли в C ++ аналог ключевого слова «где» из C#? - PullRequest
4 голосов
/ 05 мая 2020

Мне нужно создать шаблонный класс на C ++. Мне нужно убедиться, что типом параметра шаблона будет класс с 1 полем int и 1 строковым полем (полей может быть больше, но они обязательны).

Например, в C# я мог бы определить интерфейс с методами или свойствами, например:

interface MyInterface {
    int GetSomeInteger();
    string GetSomeString();
}

, а затем я мог бы использовать его в своем классе шаблона:

class MyClass<T> where T: MyInterface {}

Есть ли способ сделать что-то подобное в C ++?

Ответы [ 3 ]

8 голосов
/ 05 мая 2020

C ++ 20 предлагает вам наиболее близкое решение к C#:

#include <concepts>

template <class T>
concept MyInterface = requires(T x)
{
    { x.GetSomeInteger() } -> std::same_as<int>;
};

А затем:

template <MyInterface T>
struct MyClass
{
    // ...
};
3 голосов
/ 05 мая 2020

Наиболее распространенный способ сделать это в текущих версиях C ++ - это метод, известный как «утиная типизация».

Он просто включает использование T, как если бы он реализует интерфейса и позволить компилятору выйти из строя, если вы используете класс с несовместимым типом. выгода.

2 голосов
/ 05 мая 2020

В C ++ 20 есть концепции. Некоторые компиляторы уже поддерживают их. Например, следующее с gcc (trunk) -std=c++2a -fconcepts:

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

template<typename T>
concept HasGetIntAndString = requires(T& a) {
    { a.GetSomeInteger() } -> std::same_as<int>;
    { a.GetSomeString() } -> std::same_as<std::string>;
};

template <HasGetIntAndString T>
void bar(const T& t){
    std::cout << t.GetSomeInteger() << " " << t.GetSomeString();
}

struct foo {
    int GetSomeInteger() const { return 42; }
    std::string GetSomeString() const { return "some"; }
};

struct foo_not {
    std::string GetSomeInteger() { return "some"; }
    int GetSomeString() { return 42; }
};

int main(){
    bar( foo{});
    bar( foo_not{});
}

приводит к:

<source>: In function 'int main()':    
<source>:28:19: error: use of function 'void bar(const T&) [with T = foo_not]' with unsatisfied constraints   
   28 |     bar( foo_not{});    
      |                   ^    
<source>:12:6: note: declared here    
   12 | void bar(const T& t){    
      |      ^~~    
<source>:12:6: note: constraints not satisfied
<source>: In instantiation of 'void bar(const T&) [with T = foo_not]':    
<source>:28:19:   required from here    
<source>:6:9:   required for the satisfaction of 'HasGetIntAndString<T>' [with T = foo_not]    
<source>:6:30:   in requirements with 'T& a' [with T = foo_not]    
<source>:7:23: note: 'a.GetSomeInteger()' does not satisfy return-type-requirement    
    7 |     { a.GetSomeInteger() } -> std::same_as<int>;    
      |       ~~~~~~~~~~~~~~~~^~    
<source>:8:22: note: 'a.GetSomeString()' does not satisfy return-type-requirement    
    8 |     { a.GetSomeString() } -> std::same_as<std::string>;    
      |       ~~~~~~~~~~~~~~~^~    
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail

Live Demo

До C ++ 20 вы можно использовать SFINAE . Однако часто проще и целесообразнее не ограничивать параметр tempalte больше, чем необходимо. Если шаблон вызывает T::GetSomeInteger(), но тип T не имеет такого метода, шаблон уже не сможет скомпилировать без принятия каких-либо дополнительных мер. SFINAE в основном предназначена для предоставления более удобных сообщений об ошибках.

...