Шаблон C ++ - использовать "std :: is_same_v" вместо специализации и избежать ошибки компиляции? - PullRequest
0 голосов
/ 30 мая 2018

Я новичок в шаблонах и пытаюсь использовать их, чтобы избежать дублирования функций, которые очень похожи.

В приведенном ниже примере я сделал простой и небольшой рабочий пример, демонстрирующий мойпроблема.

В частности, у меня есть две структуры ("соло" и "дуэт").У этих структур есть общий член (a), и у одного из них есть определенный член (b).

Тогда у меня есть функция шаблона, которая может взять любую структуру и напечатать член a ... и я хотел эточтобы иметь возможность печатать элемент b, только если тип структуры - "duo".

Как я это сделал (используя std :: is_same_v), он не компилируется.Я читал, что для этого можно использовать специализацию, однако мне было интересно, если нет более элегантного способа?Потому что тогда у меня возникает ощущение, что я теряю преимущество шаблонов ... но, вероятно, я еще не понимаю всю мощь шаблонов и то, как / для чего их использовать.

Большое спасибо за вашу помощь!

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

struct solo{
  int a;     
};

struct duo : solo{
    int b;
};

template<class T>
void function(T test){
 std::cout<< std::to_string(test.a);
 if(std::is_same<T, duo>::value) std::cout<< std::to_string(test.b);
}

int main()
{
  solo testS;
  testS.a = 1;

  function(testS);
}

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

Чтобы ответить на ваш вопрос о шаблонах (хотя в данном конкретном приложении это не правильное решение по многим причинам):

Причина, по которой вы не работали, как вы писали, заключается в том, что происходит создание экземпляра шаблонаво время компиляции, и единственное, что происходит тогда, это то, что значение std::is_same вычисляется для аргумента шаблона.Таким образом, в коде для function<solo> строка

if(std::is_same<T, duo>::value) std::cout<< std::to_string(test.b);

будет иметь вид

if(false) std::cout<< std::to_string(test.b);

, который не компилируется, поскольку в test нет члена b.

Чтобы заставить его работать, вам нужно два шаблона и использовать SFINAE для выбора правильного при создании экземпляра шаблона (а поскольку шаблоны функций не могут быть частично специализированными, вам нужно написать что-то вроде следующего, что на самом делеглупый способ записать две перегрузки. Или вы можете полностью специализировать шаблон, но тогда вы не будете использовать if_same).

template<class T>
typename std::enable_if<!std::is_same<T, duo>::value, void>::type function(T test){
 std::cout<< std::to_string(test.a);
}

template<class T>
typename std::enable_if<std::is_same<T, duo>::value, void>::type function(T test){
 std::cout<< std::to_string(test.a);
 std::cout<< std::to_string(test.b);
}

Кроме того, обратите внимание, что is_same смотрит на статический тип переменной, так что если у вас есть объект solo& к duo, он все равно выберет перегрузку solo.

Несколько менее глупое использование шаблонов - написать шаблон функции, который может обрабатывать любой тип , который имеет член int b.При этом используется вспомогательная метафункция (структура, поэтому мы можем использовать частичную специализацию):

template <class T, class = int>
struct has_member_b : std::false_type {};

template <class T> 
struct has_member_b<T, decltype(std::declval<T>().b)> : std::true_type {};   

template<class T>
typename std::enable_if<has_member_b<T>::value, void>::type function(T test){
    std::cout<< std::to_string(test.a);
    std::cout<< std::to_string(test.b);
}

template<class T>
typename std::enable_if<!has_member_b<T>::value, void>::type function(T test) {
    std::cout<< std::to_string(test.a);
}

(обратите внимание, что обе версии предполагают наличие члена a, в противном случае он не будет компилироваться)

0 голосов
/ 14 февраля 2019

С введением constexpr if(cond) в C ++ 17 вы можете достичь своей цели.constexpr if(cond) оценивается во время компиляции, поэтому вы можете выбрать, что вы хотите сделать, в зависимости от типа параметра.Следующий фрагмент дает иллюстрацию.

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

struct solo{
  int a;     
};

struct duo : solo{
    int b;
};

template<class T>
void function(T test){ 
 if constexpr (std::is_same<T, duo>::value) 
    std::cout<< std::to_string(test.b)<<"\n";
 else if constexpr (std::is_same<T, solo>::value) 
    std::cout<< std::to_string(test.a)<<"\n";
}

int main()
{
  solo test1;
  test1.a = 1;

  duo test2;
  test2.b = 2;

  function(test1);
  function(test2);
}
0 голосов
/ 30 мая 2018

Для этого вам не нужен шаблон, так как duo extends solo.Просто позвоните function(solo) из function(duo):

void function(solo test) {
   std::cout << std::to_string(test.a);
}

void function(duo test) {
   function((solo) test);
   std::cout << std::to_string(test.b);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...