Час компіляції для визначення найменш дорогого типу аргументу


15

У мене є шаблон, який виглядає приблизно так

template <typename T> class Foo
{
public:
    Foo(const T& t) : _t(t) {}
private:
    const T _t;
};

Чи є розумна метапрограмування шаблону, щоб уникнути використання посилання const у випадках, коли тип аргументу тривіальний, як bool або char? подібно до:

Foo(stl::smarter_argument<T>::type t) : _t(t) {}

1
Я б не хвилювався з цього приводу, якщо функція невелика, компілятор буде вбудовувати її, і посилання навіть не існує. Якщо функція велика, крихітні витрати на введення цілого числа в посилання будуть незначними
Alan Birtles

1
Я б більше хвилювався за ідеальне пересилання, а потім уникав посилань на малі типи даних. Я здогадуюсь, що в більшості випадків проходження повного посилання на значення r може бути оптимізоване до прохідного значення.
супер

Щось слід пам’ятати, що не вказано у відповідях: те, що ви робите, переможе непрямі посібники щодо дедукції. Ви повинні пам’ятати про те, щоб написати явний посібник з дедукцією, якщо ви дбаєте про виведення аргументів шаблону класу для роботи Foo.
Брайан

Відповіді:


13

Я думаю, що правильний тип є is_scalar. Це діятиме так:

template<class T, class = void>
struct smarter_argument{
    using type = const T&;
};

template<class T>
struct smarter_argument<T, std::enable_if_t<std::is_scalar_v<T>>> {
    using type = T;
};

Редагувати:

Вище сказане все ще трохи старої школи, дякую @HolyBlackCat, що нагадав мені про цю більш стисну версію:

template<class T>
using smarter_argument_t = std::conditional_t<std::is_scalar_v<T>, T, const T&>;

не is_fundamentalпрацював би також?
Тарек Дахран

2
Скаляр @TarekDakhran включає покажчики та перерахунки, які не є основоположними, які слід передати значення IMO.
LF

Я не знайомий із синтаксисом class = void. Чи означає це, що це може бути чим завгодно, тому що це ігнорується?
cppguy

1
= voidозначає, що він має тип за замовчуванням, який є недійсним, тому використання smarter_argument<T>фактично smarter_argument<T, void>. Я залишив ім’я для цього аргументу, оскільки він нам не потрібен, отже, class = voidбез імені. Важливо, щоб std::enable_if_tу випадку, якщо він увімкнено, він також повинен бути недійсним для відповідності типу за замовчуванням.
n314159

2
Можна спростити до template <typename T> using smarter_argument = std::conditional_t<std::is_scalar_v<T>, T, const T &>;.
HolyBlackCat

3

Я б запропонував використовувати sizeof(size_t)(або sizeof(ptrdiff_t)), який повертає "типовий" розмір, пов'язаний з вашою машиною, з надією, що будь-яка змінна такого розміру вписується в реєстр. У цьому випадку ви можете сміливо передавати його за значенням. Більше того, як запропонував @ n314159 (див. Коментарі в кінці цього допису), корисно переконатися, що змінна також є trivialy_copyable.

Ось демонстрація C ++ 17:

#include <array>
#include <ccomplex>
#include <iostream>
#include <type_traits>

template <typename T>
struct maybe_ref
{
  using type = std::conditional_t<sizeof(T) <= sizeof(size_t) and
                                  std::is_trivially_copyable_v<T>, T, const T&>;
};

template <typename T>
using maybe_ref_t = typename maybe_ref<T>::type;

template <typename T>
class Foo
{
 public:
  Foo(maybe_ref_t<T> t) : _t(t)
  {
    std::cout << "is reference ? " << std::boolalpha 
              << std::is_reference_v<decltype(t)> << std::endl;
  }

private:
  const T _t;
};

int main()
{
                                                          // with my machine
  Foo<std::array<double, 1>> a{std::array<double, 1>{}};  // <- by value
  Foo<std::array<double, 2>> b{std::array<double, 2>{}};  // <- by ref

  Foo<double>               c{double{}};                // <- by value
  Foo<std::complex<double>> d{std::complex<double>{}};  // <- by ref
}

Зауважте, що немає такого поняття, як "розмір вказівника вашої машини". Запустити для прикладу це : struct Foo { void bar(){ }; int i; }; std::cout << sizeof(&Foo::i) << std::endl; //prints 8 std::cout << sizeof(&Foo::bar) << std::endl; //prints 16
BlueTune

@BlueTune Цікаво, дякую за коментар. Також див. Stackoverflow.com/a/6751914/2001017, як показано у вашому прикладі: покажчики та функціональні покажчики можуть мати різний розмір. Навіть різні покажчики можуть мати різний розмір. Ідея полягала в тому, щоб отримати «типовий» розмір машини. Я замінив неоднозначний розмір (пустота *) на sizeof (size_t)
Picaud Vincent

1
@Picaud, можливо, ви хочете використовувати <=замість цього ==, на більшості машин ваш поточний код бере, charнаприклад, посилання, якщо я бачу це правильно.
n314159

2
Ви також можете перевірити наявність Tтривіальної копії. Наприклад, загальний покажчик лише у два рази перевищує розмір size_tмоєї платформи, і його можна реалізувати лише одним вказівником, зменшуючи його до того ж розміру. Але ви обов'язково хочете взяти shared_ptr за const ref, а не за значенням.
n314159

@ n314159 так, це буде вдосконаленням. Ви добре, якщо включите свою ідею у свою відповідь?
Піко Вінсент

2

Я б скористався ключовим словом C ++ 20 requires. Ось так:

#include <iostream>

template<typename T>
class Foo
{
public:
    Foo(T t) requires std::is_scalar_v<T>: _t{t} { std::cout << "is scalar" <<std::endl; }
    Foo(const T& t) requires (not std::is_scalar_v<T>): _t{t} { std::cout << "is not scalar" <<std::endl;}
private:
    const T _t;
};

class cls {};

int main() 
{
    Foo{true};
    Foo{'d'};
    Foo{3.14159};
    cls c;
    Foo{c};

    return 0;
}

Ви можете запустити код в Інтернеті, щоб побачити наступний вихід:

is scalar
is scalar
is scalar
is not scalar

Цікаво. Чи є користь від використання const auto та аргументу конструктора?
cppguy

@cppguy: Я радий, що ти задаєш це питання. Якщо я заміню аргумент "const auto & t" на "const T & t", код не буде компілюватися. Помилка читає "... неоднозначне виведення для аргументів шаблону 'Foo' ...". Можливо, ви можете дізнатися, чому?
BlueTune

1
@cppguy: Наша дискусія спричинила запитання, яке я поставив. Ви можете знайти його тут .
BlueTune

1
Поняття тут надмірно важкі для читання, ніж альтернатива.
СС Енн

1
@SS Anne: ІМХО з використанням концепцій C ++ 20 ніколи не є зайвим. Це просто елегантно. IMHO альтернативи, які я бачив до цього часу, важче прочитати, оскільки використання вкладених шаблонів.
BlueTune
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.