Способ времени компиляции для определения наименее дорогого типа аргумента - PullRequest
15 голосов
/ 08 марта 2020

У меня есть шаблон, который выглядит следующим образом

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

Есть ли хитрый способ метапрограммирования шаблонов, чтобы избежать использования константной ссылки в случаях, когда тип аргумента тривиален, как bool или char? как:

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

Ответы [ 3 ]

13 голосов
/ 08 марта 2020

Я думаю, что правильная черта типа 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&>;
3 голосов
/ 08 марта 2020

Я бы предложил использовать 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
}
2 голосов
/ 08 марта 2020

Я бы использовал ключевое слово 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...