Let's say, I have my List<T> class. I have a lot of functions where I have to pass a single object of my T type. For instance
void add(const T& item)
{
...
}
and it makes sense if T is some class or a struct. However, if T is a byte or integer, it's pointless or even wrong to pas it via reference, since memory pointer costs 8 bytes (4 on 32 bit system), i.e. I pass 1 byte size data type via 8 byte size pointer.
And so I decided to define argument data type using using directive. Kind of:
using argType = const T&; requires sizeof(T) > 8
using argType = T; requires sizeof(T) <= 8
But, obviously, this code doesn't work. Can you, please, propose me other solutions for that?
It sounds like what you need is conditional_t:
#include <type_traits>
template<class T>
class List {
using argType = std::conditional_t<(sizeof(T) > 8), const T&, T>;
void add(argType item) { }
};
To add to the answer provided here: https://stackoverflow.com/a/69864339/2963099 where conditional_t was suggested
It is important to make sure that items with non-trivial copy constructors are passed by reference, since a value copy can be very expensive. As such I would limit pass by value to trivially copyable objects.
Also using sizeof(T*) will work more generically than a hardcoded 8
Note that args are reversed and the test is to use pass by value, and objects with sizeof == 8 can still be pass by reference if needed.
using argType = std::conditional_t<
(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>,
T, const T&>;
An example of usage:
#include <type_traits>
#include <vector>
#include <iostream>
struct Test
{
Test() : x{0}
{
std::cout << "Default construct" << std::endl;
}
Test(const Test& rhs) : x{rhs.x}
{
std::cout << "copy: " << x << std::endl;
}
int * x;
};
template<class T>
struct List {
using argType = std::conditional_t<(sizeof(T) <= sizeof(T *)) && std::is_trivially_copyable_v<T>, T, const T&>;
static argType copy(argType item)
{
std::cout << "Running Copy" << std::endl;
return item;
}
};
auto foo(typename List<Test>::argType t)
{
return List<Test>::copy(t);
}
auto goo(typename List<unsigned long long>::argType t)
{
return List<unsigned long long>::copy(t);
}
int main()
{
Test t;
std::cout << "Calling Test Copy" << std::endl;
foo(t);
std::cout << "After Test Copy" << std::endl;
unsigned long long u;
std::cout << "Calling ULL Copy" << std::endl;
goo(u);
std::cout << "After ULL Copy" << std::endl;
}
see it here: https://godbolt.org/z/WTKT8G6rh
Note, you can see the parameter of foo() and goo() easily here
Note how Test would have been passed by value and incurred a possibly expensive copy, but checking for an expensive copy prevents this
With c++20 concepts you can add some constraints to your templates,
from en.cppreference.com/w/cpp/language/constraints
Class templates, function templates, and non-template functions (typically members of class templates) may be associated with a constraint, which specifies the requirements on template arguments, which can be used to select the most appropriate function overloads and template specializations.
Named sets of such requirements are called concepts. Each concept is a predicate, evaluated at compile time, and becomes a part of the interface of a template where it is used as a constraint:
#include <string>
#include <cstddef>
#include <concepts>
// Declaration of the concept "Hashable", which is satisfied by any type 'T'
// such that for values 'a' of type 'T', the expression std::hash<T>{}(a)
// compiles and its result is convertible to std::size_t
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
struct meow {};
// Constrained C++20 function template:
template<Hashable T>
void f(T) {}
//
// Alternative ways to apply the same constraint:
// template<typename T>
// requires Hashable<T>
// void f(T) {}
//
// template<typename T>
// void f(T) requires Hashable<T> {}
int main() {
using std::operator""s;
f("abc"s); // OK, std::string satisfies Hashable
//f(meow{}); // Error: meow does not satisfy Hashable
}
In your case I think you can constraint your template as follows:
template<typename T>
concept InfTo8Bytes = requires(T a) {
sizeof (T) <= 8;
};
and use you concepts as follows:
template<InfTo8Bytes T>
class Foo {
};
Related
I'm playing around with C++ concepts and came across an interesting problem. I have the following two custom-defined concepts:
template<typename T>
concept is_dereferencable = requires (T t) { *t; };
template<typename T>
concept is_printable = requires (T t) { std::cout << t; };
As the names suggest, the first one is used to determine if a given type can be dereferenced, while the other one to check if a type supports output operator. I also have a function template called println, which looks like this:
template<typename T>
void println(const T& t)
{
if constexpr (is_dereferencable<T>) {
if constexpr (is_printable<decltype(*t)>) {
std::cout << *t << '\n';
}
} else if constexpr (is_printable<T>) {
std::cout << t << '\n';
}
}
This prints the dereferenced value *t if and only if type T is dereference-able and the type of the dereferenced value is printable. So, for example, I can use this function template with something like an std::optional:
int main
{
std::optional<std::string> stringOpt {"My Optional String"};
::println(stringOpt);
return 0;
}
This will print My Optional String as expected. While this is nice, the function will just silently print nothing if the type of a dereferenced value of a derefernce-able is not printable. So, for a user-defined type Person, the following will just print nothing:
struct Person
{
std::string m_name;
explicit Person(const std::string& name) : m_name {name} {}
};
int main
{
std::optional<Person> personOpt {"John Doe"}
::println(personOpt);
return 0;
}
So I would like to move the above compile-time ifs to a requires clause itself in order to get compile time errors in such cases. Is there a way to achieve that? Is there a way to get the dereferenced type of a given template type T? To make it a bit clearer, I would like to have something like this:
template<typename T>
requires is_dereferencable<T> && is_printable<decltype(*T)>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
P.S.: I understand that I could remove the nested if and just fail upon trying to call an output operator on something that doesn't support it. However, I want to specifically move this compile-time error to the concept to get a clearer error message.
another option is put the constraint after parameters, so it has access to them.
template<typename T>
void printDereferencable(const T& t)
requires is_dereferencable<const T&> && is_printable<decltype(*t)>
{
std::cout << *t << '\n';
}
note: it should test on const T&, not T
you can use std::declval
template<typename T>
requires is_dereferencable<const T&> && is_printable<decltype(*std::declval<const T&>())>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
or you can just write a is_dereference_printable concept
template<typename T>
concept is_dereference_printable = requires (T t) { std::cout << *t; };
This question already has answers here:
How to define a template member function of a template class [duplicate]
(2 answers)
Why can templates only be implemented in the header file?
(17 answers)
Closed 7 months ago.
So i made a template struct cause i want to be able to decide what type i give to my val. But when creating a function i don't know how to do it.
Here's what i'm doing:
In my .hpp
template<typename T>
struct Integer
{
T val;
void setUint(const T &input);
};
Now i can set what variable i want in the val and what i want in the function.
But now in my cpp i don't know how to invoke the function.
void Integer<T>::setUint(const T &input)
{
val = input;
}
Error: identifier "T" is undefined.
A template function is a way to operate with generic types (you may consider the type as an argument). Your template parameter T allows to pass different types to a function when you invoke the function (which means, simply said, you may replace T with some other types int, double, ...)
Please, have a look at the following simple example.
#include <iostream>
#include <typeinfo>
// you may put it all in a hpp and thus include the hpp file in the CPP
template<typename T>
struct Integer
{
T val;
void setUint(const T &input){
val=input;
std::cout <<"the type is " << typeid(T).name() << " value is "<< val << std::endl;
}
};
// or look at Jarod42's implementation details.
/*
template<typename T>
void Integer<T>::setUint(const T &input){
val=input;
std::cout <<"the type is " << typeid(T).name() << " value is "<< val << std::endl;
}*/
// and here you have your cpp calling you template function with different types
int main()
{
Integer<double> value;
value.setUint(1500000);
Integer<int> value2;
value2.setUint(5);
return 0;
}
Syntax is
template <typename T>
void Integer<T>::setUint(const T &input)
{
val = input;
}
Also for templates, check if T actually matches your expectations. Your template will not make sense for types that are not Integers. Example on how to add these constraints here : https://godbolt.org/z/YsneKe7vj (both C++17 SFINAE, and C++20 concept)
#include <type_traits>
#include <string>
//-----------------------------------------------------------------------------------------------------------
// for C++17 setup a constexpr that will evaluate (at compile time, that's the constexpr bit)
// if a given type is an integer type
template<typename type_t>
constexpr bool is_integer_type_v = std::is_integral_v<type_t> && !std::is_same_v<type_t,bool>;
// create a template that can optionally be enabled. This is an example of a technique called SFINAE
template<typename type_t, typename enable = void>
struct Integer;
// for C++ define a specialization that will succesfully be enabled of integer types only
template<typename type_t>
struct Integer<type_t,std::enable_if_t<is_integer_type_v<type_t>>>
{
void Set(const type_t v)
{
value = v;
}
type_t value;
};
//-----------------------------------------------------------------------------------------------------------
// for C++20 use concepts
template<typename type_t>
concept is_integer = std::is_integral_v<type_t> && !std::is_same_v<type_t, bool>;
// note is_integer now replaces typename, and it can only accept types
// that satisfy the concept
template<is_integer integer_t>
struct IntegerCpp20
{
void Set(const integer_t v)
{
value = v;
}
integer_t value;
};
//-----------------------------------------------------------------------------------------------------------
int main()
{
//Integer<std::string> value; // will not compile;
Integer<int> value;
value.Set(2);
//IntegerCpp20<std::string> value; // will not compile;
IntegerCpp20<int> value20;
value20.Set(20);
return 0;
}
Looking to get some detailed explanations as to why the following is allowed / disallowed.
#include <type_traits>
#include <iostream>
template<typename T>
std::enable_if_t<std::is_arithmetic_v<T>> foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>> foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
In relation to the above code sample, why is this allowed as overload resolution would not kick in as the functions differ only by return type?
Following on from the above, with Expression SFINAE I have to make sure the i disambiguate the overloads as in the following code snippet.
#include <type_traits>
#include <iostream>
template<typename T, std::enable_if_t<std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "arithmetic version called\n";
}
template<typename T, std::enable_if_t<!std::is_arithmetic_v<T>>* p = nullptr>
void foo(T v) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
So I guess this question is more of a why it works, ie language rules vs how to do it. I have been using these techniques for a while and often still scratch my head on which bits of the languages rules enable this.
Thanks in advance.
#include <type_traits>
#include <iostream>
#include <string>
template<typename T>
auto increment_or_self_(T a, int i) -> std::decay_t<decltype(++std::declval<T&>(), std::declval<T>())> {
++a;
return a;
}
template<typename T>
auto increment_or_self_(T a, ...) -> T {
return a;
}
template<typename T>
T increment_or_self(T a) {
return increment_or_self_(a, 0);
}
int main() {
std::cout << increment_or_self(33) << std::endl;
std::cout << increment_or_self(std::string("hello")) << std::endl;
}
In the above increment_or_self has a dummy arg to ensure the overloads are not ambiguous.
Also why do we have to use pointers when we move the EnableIf as shown in the following?
Your first example has no problems with overload resolution, because you do not have two times the same function with different return types, because the SFINAE stuff already disables one of both in each of the instantiations. Even if your SFINAE expression will result in two different return types, it simply doesn't matter, because both instantiated functions will have different signatures as they have different input parameters.
I modified your example and this will also be well formed ( see different return types with this change )
std::enable_if_t<std::is_arithmetic_v<T>, float> foo(T ) {
std::cout << "arithmetic version called\n";
return 1.1;
}
template<typename T>
std::enable_if_t<!std::is_arithmetic_v<T>, int> foo(T ) {
std::cout << "non arithmetic version called\n";
return 1;
}
int main() {
foo(33);
foo("hello");
std::cout << std::is_same_v< float, decltype(foo(33))> << std::endl;
std::cout << std::is_same_v< int, decltype(foo("Hallo"))> << std::endl;
}
Your question: "Also why do we have to use pointers when we move the EnableIf as shown in the following?"
Quite simple: The std::enable_if with only one parameter ends in a void. And you simply can't assign a value to void. You can change your code to std::enable_if_t<!std::is_arithmetic_v<T>, bool> p = true as now your std::enable_if results in a bool where you can assign a value like true.
And with C++20 you can simplify a lot by using concepts
template <typename T> concept arithmetic = std::is_arithmetic_v<T>;
template< arithmetic T>
void foo(T) {
std::cout << "arithmetic version called\n";
}
void foo(auto) {
std::cout << "non arithmetic version called\n";
}
int main() {
foo(33);
foo("hello");
}
As you can see, we do not need to define a "non-arithmetic" concept, as we simply can trust on the rule, that the more specified template will be used, if more templates are able to be instantiated.
Note you didn't specify different return values, both functions return a void.
So there is no problem there. SFINAE will just ensure one of the functions will not result in a compilable return type (but will not give an error, since substition is not an error).
Don't get to focused on SFINAE, c++ keeps improving meta template programming techniques and in this case I would just use an if constexpr solution. Like this:
#include <type_traits>
#include <iostream>
template<typename T>
void foo(const T& v) // <== I prefer not to use pass by value for template parameters so I pass by reference (avoid copying)
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic version called\n";
}
else
{
std::cout << "non arithmetic version called\n";
}
}
int main()
{
foo(33);
foo("hello");
return 0;
}
I am trying to get a better understanding of std::enable_if in C++11 and have been trying to write a minimal example: a class A with a member function void foo() that has different implementations based on the type T from the class template.
The below code gives the desired result, but I am not understanding it fully yet. Why does version V2 work, but not V1? Why is the "redundant" type U required?
#include <iostream>
#include <type_traits>
template <typename T>
class A {
public:
A(T x) : a_(x) {}
// Enable this function if T == int
/* V1 */ // template < typename std::enable_if<std::is_same<T,int>::value,int>::type = 0>
/* V2 */ template <typename U=T, typename std::enable_if<std::is_same<U,int>::value,int>::type = 0>
void foo() { std::cout << "\nINT: " << a_ << "\n"; }
// Enable this function if T == double
template <typename U=T, typename std::enable_if<std::is_same<U,double>::value,int>::type = 0>
void foo() { std::cout << "\nDOUBLE: " << a_ << "\n"; }
private:
T a_;
};
int main() {
A<int> aInt(1); aInt.foo();
A<double> aDouble(3.14); aDouble.foo();
return 0;
}
Is there a better way to achieve the desired result, i.e. for having different implementations of a void foo() function based on a class template parameter?
I know this wont fully answer your question, but it might give you some more ideas and understanding of how you can use std::enable_if.
You could replace your foo member functions with the following and have identical functionality:
template<typename U=T> typename std::enable_if<std::is_same<U,int>::value>::type
foo(){ /* enabled when T is type int */ }
template<typename U=T> typename std::enable_if<std::is_same<U,double>::value>::type
foo(){ /* enabled when T is type double */ }
A while back I gained a pretty good understanding of how enable_if works, but sadly I have forgotten most of its intricacies and just remember the more practical ways to use it.
As for the first question: why V1 doesn't work? SFINAE applies only in overload resolution - V1 however causes error at the point where type A is instantiated, well before foo() overload resolution.
I suppose there are lot's of possible implementations - which is the most appropriate depends on an actual case in question. A common approach would be to defer the part of A that's different for different template types to a helper class.
template <typename T>
class A_Helper;
template <>
class A_Helper<int> {
public:
static void foo( int value ){
std::cout << "INT: " << value << std::endl;
}
};
template <>
class A_Helper<double> {
public:
static void foo( double value ){
std::cout << "DOUBLE: " << value << std::endl;
}
};
template <typename T>
class A {
public:
A( T a ) : a_(a)
{}
void foo(){
A_Helper<T>::foo(a_);
}
private:
T a_;
};
The rest of A can be declared only once in a generic way - only the parts that differ are deferred to a helper. There is a lot of possible variations on that - depending on your requirements...
A (somewhat) outdated article explores ways to use decltype along with SFINAE to detect if a type supports certain operators, such as == or <.
Here's example code to detect if a class supports the < operator:
template <class T>
struct supports_less_than
{
static auto less_than_test(const T* t) -> decltype(*t < *t, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
This outputs true, since of course std::string supports the < operator. However, if I try to use it with a class that doesn't support the < operator, I get a compiler error:
error: no match for ‘operator<’ in ‘* t < * t’
So SFINAE is not working here. I tried this on GCC 4.4 and GCC 4.6, and both exhibited the same behavior. So, is it possible to use SFINAE in this manner to detect whether a type supports certain expressions?
In C++11 the shortest most general solution I found was this one:
#include <type_traits>
template<class T, class = decltype(std::declval<T>() < std::declval<T>() )>
std::true_type supports_less_than_test(const T&);
std::false_type supports_less_than_test(...);
template<class T> using supports_less_than = decltype(supports_less_than_test(std::declval<T>()));
#include<iostream>
struct random_type{};
int main(){
std::cout << supports_less_than<double>::value << std::endl; // prints '1'
std::cout << supports_less_than<int>::value << std::endl; // prints '1'
std::cout << supports_less_than<random_type>::value << std::endl; // prints '0'
}
Works with g++ 4.8.1 and clang++ 3.3
A more general solution for arbitrary operators (UPDATE 2014)
There is a more general solution that exploits the fact that all built-in operators are also accessible (and posibly specialized) through STD operator wrappers, such as std::less (binary) or std::negate (unary).
template<class F, class... T, typename = decltype(std::declval<F>()(std::declval<T>()...))>
std::true_type supports_test(const F&, const T&...);
std::false_type supports_test(...);
template<class> struct supports;
template<class F, class... T> struct supports<F(T...)>
: decltype(supports_test(std::declval<F>(), std::declval<T>()...)){};
This can be used in a quite general way, especially in C++14, where type deduction is delayed to the operator wrapper call ("transparent operators").
For binary operators it can be used as:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::less<>(double, double)>::value << std::endl; // '1'
std::cout << supports<std::less<>(int, int)>::value << std::endl; // '1'
std::cout << supports<std::less<>(random_type, random_type)>::value << std::endl; // '0'
}
For unary operators:
#include<iostream>
struct random_type{};
int main(){
std::cout << supports<std::negate<>(double)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(int)>::value << std::endl; // '1'
std::cout << supports<std::negate<>(random_type)>::value << std::endl; // '0'
}
(With the C++11 standard library is a bit more complicated because there is no failure on instatiating decltype(std::less<random_type>()(...)) even if there is no operation defined for random_type, one can implement manually transparent operators in C++11, that are standard in C++14)
The syntax is quite smooth. I hope something like this is adopted in the standard.
Two extensions:
1) It works to detect raw-function applications:
struct random_type{};
random_type fun(random_type x){return x;}
int main(){
std::cout << supports<decltype(&fun)(double)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(int)>::value << std::endl; // '0'
std::cout << supports<decltype(&fun)(random_type)>::value << std::endl; // '1'
}
2) It can additionally detect if the result is convertible/comparable to a certain type, in this case double < double is supported but a compile-time false will be returned because the result is not the specified one.
std::cout << supports<std::equal_to<>(std::result_of<std::less<>(double, double)>::type, random_type)>::value << std::endl; // '0'
Note: I just tried to compile the code with C++14 in http://melpon.org/wandbox/ and it didn't work. I think there is a problem with transparent operators (like std::less<>) in that implementation (clang++ 3.5 c++14), since when I implement my own less<> with automatic deduction it works well.
You need to make your less_than_test function a template, since SFINAE stands for Substitution Failure Is Not An Error and there's no template function that can fail selection in your code.
template <class T>
struct supports_less_than
{
template <class U>
static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
This is C++0x, we don't need sizeof-based tricks any more... ;-]
#include <type_traits>
#include <utility>
namespace supports
{
namespace details
{
struct return_t { };
}
template<typename T>
details::return_t operator <(T const&, T const&);
template<typename T>
struct less_than : std::integral_constant<
bool,
!std::is_same<
decltype(std::declval<T const&>() < std::declval<T const&>()),
details::return_t
>::value
> { };
}
(This is based on iammilind's answer, but doesn't require that T's operator< return-type be a different size than long long and doesn't require that T be default-constructable.)
Below simple code satisfies your requirement (if you don't want compile error):
namespace supports {
template<typename T> // used if T doesn't have "operator <" associated
const long long operator < (const T&, const T&);
template <class T>
struct less_than {
T t;
static const bool value = (sizeof(t < t) != sizeof(long long));
};
}
Usage:
supports::less_than<std::string>::value ====> true; // ok
supports::less_than<Other>::value ====> false; // ok: no error
[Note: If you want compile error for classes not having operator < than it's very easy to generate with very few lines of code.]
#xDD is indeed correct, though his example is slightly erroneous.
This compiles on ideone:
#include <array>
#include <iostream>
struct Support {}; bool operator<(Support,Support) { return false; }
struct DoesNotSupport{};
template <class T>
struct supports_less_than
{
template <typename U>
static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
{ }
static std::array<char, 2> less_than_test(...) { }
static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};
int main()
{
std::cout << std::boolalpha << supports_less_than<Support>::value << std::endl;
std::cout << std::boolalpha <<
supports_less_than<DoesNotSupport>::value << std::endl;
}
And results in:
true
false
See it here in action.
The point is that SFINAE only applies to template functions.