I have a template that looks like this
template <typename T> class Foo
{
public:
Foo(const T& t) : _t(t) {}
private:
const T _t;
};
Is there a savvy template metaprogramming way to avoid using a const reference in cases where the argument type is trivial like a bool or char? like:
Foo(stl::smarter_argument<T>::type t) : _t(t) {}
I think the right type trait is is_scalar
. This would work as follows:
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;
};
Edit:
The above is still a bit old-school, thanks @HolyBlackCat for reminding me of this more terse version:
template<class T>
using smarter_argument_t = std::conditional_t<std::is_scalar_v<T>, T, const T&>;
I would suggest to use sizeof(size_t)
(or sizeof(ptrdiff_t)
) which returns a "typical" size related to your machine with the hope that any variable of this size fits into a register. In that case you can safely pass it by value. Moreover, as suggested by @n314159 (see comments at the end of this post) it is useful to ensure that the variable is also trivialy_copyable
.
Here is a C++17 demo:
#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
}
I would make use of the C++20 keyword requires
. Just like that:
#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;
}
You can run the code online to see the following output:
is scalar
is scalar
is scalar
is not scalar
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With