CTAD doesn't work with defaulted template arguments? - c++

Compare the following case when I have a class object that takes a vector. The non-deduced parameter T can be substituted fine with the default template argument:
#include <vector>
template <typename T = int>
struct container
{
container(std::vector<T> vec) {}
};
int main()
{
container C = std::vector{1,2,3,4,5};
}
This is not the case for my class which is a bit more complicated (CompilerExplorer):
#include <cstdio>
#include <initializer_list>
#include <variant>
template <size_t> struct obj;
template<size_t Vs>
using val = std::variant<std::monostate, int, struct obj<Vs>>;
template <size_t Vs = 0>
struct obj
{
obj() = default;
obj(std::initializer_list<val<Vs>> init) {
printf("initializer of object called, Vs = %d\n", Vs);
}
};
template <size_t Vs = 0>
struct container : public obj<Vs>
{
container(obj<0> init) {}
};
int main()
{
container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
}
This fails with the following error:
<source>: In function 'int main()':
<source>:29:57: error: class template argument deduction failed:
29 | container<5> some_container = obj{1,2,5,2,obj{1,2,33},2,2};
| ^
<source>:29:57: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
But it works when I supplement the template specialization obj<0> in the instantiation of the container (in main). Any ideas why this doesn't work for my class and how I can fix it? I don't want to force the user to specify the template each time.

This problem already exists in the simpler case of just
auto o = obj{1,2,33};
which yields this error:
<source>:29:24: error: class template argument deduction failed:
29 | auto o = obj{1,2,33};
| ^
<source>:29:24: error: no matching function for call to 'obj(int, int, int)'
<source>:14:5: note: candidate: 'template<long unsigned int Vs> obj(std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >)-> obj<<anonymous> >'
14 | obj(std::initializer_list<val<Vs>> init) {
| ^~~
<source>:14:5: note: template argument deduction/substitution failed:
<source>:29:24: note: mismatched types 'std::initializer_list<std::variant<std::monostate, int, obj<Vs> > >' and 'int'
29 | auto o = obj{1,2,33};
So, the compiler is unable to deduce, that the three ints should be an initializer list. If you add extra braces around them, the compiler recognizes that this should actually be a single list argument instead of three separate ones and it works:
auto o = obj{{1,2,33}};
This also carries over to the more complicated case:
container some_container = obj{{1,2,5,2,obj{{1,2,33}},2,2}};

Related

Tagged structures casting rules

I was playing around with toy tuple implementations and eventually stuck with how get function works.
Consider this simple example
#include <iostream>
#include <utility>
template <size_t Tag, typename ValueType>
struct TagedValue { ValueType value; };
struct Test : TagedValue<0, int>, TagedValue<1, std::string>, TagedValue<2, double> {};
template <size_t Idx, typename T>
auto& get(Test& test) {
((TagedValue<Idx, T>&)(test)).value;
}
template <size_t Idx, typename T>
auto& get_impl(TagedValue<Idx, T>& tagged_value) {
return tagged_value.value;
}
template <size_t Idx>
auto& get_2(Test& test) {
return get_impl<Idx>(test);
}
int main()
{
Test test;
get_2<0>(test);
get<0>(test);
}
I get this error:
<source>: In function 'int main()':
<source>:29:16: error: no matching function for call to 'get<0>(Test&)'
29 | get<0>(test);
| ^
<source>:10:7: note: candidate: 'template<long unsigned int Idx, class T> auto& get(Test&)'
10 | auto& get(Test& test) {
| ^~~
<source>:10:7: note: template argument deduction/substitution failed:
<source>:29:16: note: couldn't deduce template parameter 'T'
29 | get<0>(test);
| ^
I do have couple of questions:
Basically why get_2 works and get doesn't compile. To me it looks like get_2 does exactly what I'm trying to do inside get
Does deducing T for get_2 take O(1) time, if yes how is it possible? Does compiler store some kind of map internally?
When you call a function template then all template arguments must either be specified explicitly or be deduced from the function arguments. When you call
get<0>(test);
Then Idx is 0, but there is no way for the compiler to know what T is supposed to be. The parameter is just Test, and T cannot be deduced from that.

Partial specialiszation of a template class with string template argument

#include <iostream>
template<unsigned N>
struct FixedString
{
char buf[N + 1]{};
constexpr FixedString(const char (&s)[N])
{
for (unsigned i = 0; i != N; ++i)
buf[i] = s[i];
}
};
template<int, FixedString name>
class Foo
{
public:
auto hello() const { return name.buf; }
};
template<FixedString name>
class Foo<6, name>
{
public:
auto hello() const { return name.buf; }
};
int main()
{
Foo<6, "Hello!"> foo;
foo.hello();
}
I'm trying to add a template specialisation Foo<6, name> and it ends in this error:
macros.h: At global scope:
macros.h:3:18: error: class template argument deduction failed:
23 | class Foo<6, name>
| ^
macros.h:3:18: error: no matching function for call to ‘FixedString(FixedString<...auto...>)’
macros.h:7:15: note: candidate: ‘template<unsigned int N> FixedString(const char (&)[N])-> FixedString<N>’
7 | constexpr FixedString(const char (&s)[N])
| ^~~~~~~~~~~
macros.h:7:15: note: template argument deduction/substitution failed:
macros.h:13:18: note: mismatched types ‘const char [N]’ and ‘FixedString<...auto...>’
23 | class Foo<6, name>
| ^
macros.h:4:8: note: candidate: ‘template<unsigned int N> FixedString(FixedString<N>)-> FixedString<N>’
4 | struct FixedString
| ^~~~~~~~~~~
macros.h:4:8: note: template argument deduction/substitution failed:
macros.h:13:18: note: mismatched types ‘FixedString<N>’ and ‘FixedString<...auto...>’
23 | class Foo<6, name>
What is the proper way to specialise template classes with string template arguments?
g++ (Ubuntu 9.4.0-1ubuntu1~16.04) 9.4.0
What is the proper way to specialise template classes with string template arguments?
The given code is well-formed(in C++20) but fails to compile for gcc 10.2 and lower. It however compiles fine from gcc 10.3 and higher. Demo
Might be due to that not all C++20 features were fully implemented in gcc 10.2 and lower.

Template argument deduction fails when passing enum

I'm trying to insert an enum parameter into a constexpr function. I have done this in the past and it always worked... except in this case. This case is only special in that I'm calling a factory function first. But apparently C++ doesn't see through this. What can be done?
Those are my errors:
<source>: In function 'constexpr auto operator+(some_enum)':
<source>:30:28: error: no matching function for call to 'signal<1, state>(std::nullptr_t)'
30 | return signal<1, state>(nullptr);
| ~~~~~~~~~~~~~~~~^~~~~~~~~
<source>:23:37: note: candidate: 'template<bool set, some_enum S, class ... Ts> constexpr signal_str<sizeof... (Ts)> signal(Ts ...)'
23 | constexpr signal_str<sizeof...(Ts)> signal(Ts... Args)
| ^~~~~~
<source>:23:37: note: template argument deduction/substitution failed:
<source>:30:28: error: 'state' is not a constant expression
30 | return signal<1, state>(nullptr);
| ~~~~~~~~~~~~~~~~^~~~~~~~~
<source>:30:28: note: in template argument for type 'some_enum'
<source>:28:16: error: invalid return type 'auto' of 'constexpr' function 'constexpr auto operator+(some_enum)'
28 | constexpr auto operator+(some_enum state)
| ^~~~~~~~
Compiler returned: 1
This is my code:
#include <array>
#include <cstdint>
#include <iostream>
typedef void* TaskType_t;
enum some_enum
{
SOME_STATE = 1,
};
template <size_t N>
struct signal_str
{
uint32_t val_;
std::array<TaskType_t, N> tasks_;
};
template <bool set, some_enum S, typename... Ts>
constexpr signal_str<sizeof...(Ts)> signal(Ts... Args)
{
return signal_str<sizeof...(Ts)>{S, {Args...}}.val_;
}
constexpr auto operator+(some_enum state)
{
return signal<1, state>(nullptr);
}
int main()
{
static_assert(+SOME_STATE);
}
I'm using C++17 on xtensa-gcc 8.2.0 but it's the same with gcc 11 (LIVE DEMO).
EDIT: This problem is different from "Why is const variable necessary for template specialization over constants" because enums are already constants. To showcase this the following DOES actually compile:
#include <array>
#include <cstdint>
#include <iostream>
typedef void* TaskType_t;
enum some_enum
{
SOME_STATE = 1,
};
template <size_t N>
struct signal_str
{
uint32_t val_;
std::array<TaskType_t, N> tasks_;
};
constexpr auto operator+(some_enum state)
{
return signal_str<1>{state, nullptr}.val_;
}
int main()
{
static_assert(+SOME_STATE);
}
DEMO
So the problem IMHO is not the enum..

c++ type traits : ensuring a subclass implements a method

There is a virtual class C.
I would like to ensure that any concrete subclass inheriting from C implements a function "get" (and have a clear compile time error if one does not)
Adding a virtual "get" function to C would not work in this case, as C subclasses could implement get functions of various signatures.
(in the particular case I am working on, pybind11 will be used to creates bindings of the subclasses, and pybind11 is robust of the "get" method of B to have a wide range of signatures)
Checking at compile time if a class has a function can be done with type traits, e.g.
template<class T>
using has_get =
decltype(std::declval<T&>().get(std::declval<int>()));
My question is where in the code should I add a static assert (or smthg else) to check the existence of the "get" function. Ideally, this should be part of C declaration, as things should be easy for new user code inheriting from it. It may also be that a completely different approach would be better, which I'd like to hear.
Not sure what standard you are using but with C++20 you can do something like this using concepts
template<typename T>
concept HasGet = requires (T a)
{
a.get();
};
template<HasGet T>
void foo(T x)
{
x.get();
}
struct Foo
{
int get() {
return 1;
}
};
struct Bar
{
};
int main()
{
foo(Foo{});
foo(Bar{});
}
Error:
<source>: In function 'int main()':
<source>:27:12: error: use of function 'void foo(T) [with T = Bar]' with unsatisfied constraints
27 | foo(Bar{});
| ^
<source>:8:6: note: declared here
8 | void foo(T x)
| ^~~
<source>:8:6: note: constraints not satisfied
<source>: In instantiation of 'void foo(T) [with T = Bar]':
<source>:27:12: required from here
<source>:2:9: required for the satisfaction of 'HasGet<T>' [with T = Bar]
<source>:2:18: in requirements with 'T a' [with T = Bar]
<source>:4:9: note: the required expression 'a.get()' is invalid
4 | a.get();
EDIT:
As C++14 is preferred, if I understand you requirements, this is something you can do in C++14
#include <type_traits>
#include <utility>
using namespace std;
template<typename... Ts>
using void_t = void;
template<typename T, typename = void>
struct has_get
: false_type
{};
template<typename T>
struct has_get<T, void_t<decltype(declval<T>().get())>>
: true_type
{};
template<typename T>
static constexpr auto has_get_v = has_get<T>::value;
struct P
{
};
struct C1 : P
{
int get()
{
return 1;
}
};
struct C2 : P
{
float get()
{
return 1.0F;
}
};
struct C3
{
bool get()
{
return true;
}
};
template<typename T>
enable_if_t<is_base_of<P, decay_t<T>>::value && has_get_v<decay_t<T>>> foo(T x)
{
x.get();
}
int main()
{
foo(C1{});
foo(C2{});
foo(C3{});
}
ERROR:
<source>: In function 'int main()':
<source>:61:11: error: no matching function for call to 'foo(C3)'
61 | foo(C3{});
| ^
<source>:52:77: note: candidate: 'template<class T> std::enable_if_t<(std::is_base_of<P, typename std::decay<_Tp>::type>::value && has_get<typename std::decay<_Tp>::type>::value)> foo(T)'
52 | enable_if_t<is_base_of<P, decay_t<T>>::value && has_get<decay_t<T>>::value> foo(T x)
| ^~~
<source>:52:77: note: template argument deduction/substitution failed:
In file included from <source>:1:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
<source>:52:77: required by substitution of 'template<class T> std::enable_if_t<(std::is_base_of<P, typename std::decay<_Tp>::type>::value && has_get<typename std::decay<_Tp>::type>::value)> foo(T) [with T = C3]'
<source>:61:11: required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2554:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
2554 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;

Make Templates for basic data types only

How can we make a template accept only basic data types.
template <typename T>
void GetMaxValue( T& x )
{
//... Finds max Value
}
In the above function GetMaxValue we are able to pass any value without any an error.
But the std Function std::numeric_limits<T>::max() has handled it.
For example:
auto max = std::numeric_limits< std::map<int,int> >::max();
will Give an error error C2440: '<function-style-cast>' : cannot convert from 'int' to 'std::map<_Kty,_Ty>'
With constraints in C++20:
#include <type_traits>
template <class T>
requires std::is_arithmetic_v<T>
void GetMaxValue( T& x )
{
//... Finds max Value
}
Usage:
int a = 0;
GetMaxValue(a); // fine
std::vector<int> b;
GetMaxValue(b); // compiler error
Demo
With std::enable_if otherwise:
template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
void GetMaxValue( T& x )
{
//... Finds max Value
}
Demo 2
The error messages pre-constraints are harder to read:
error: no matching function for call to 'GetMaxValue(std::vector<int>&)'
| GetMaxValue(b); // compiler error
| ^
Note: candidate: 'template<class T, typename std::enable_if<is_arithmetic_v<T>, int>::type <anonymous> > void GetMaxValue(T&)'
| void GetMaxValue( T& x )
| ^~~~~~~~~~~
note: template argument deduction/substitution failed:
error: no type named 'type' in 'struct std::enable_if<false, int>'
| template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
| ^
In instantiation of 'void GetMaxValue(T&) [with T = int; typename std::enable_if<is_arithmetic_v<T>, int>::type <anonymous> = 0]'
vs
error: cannot call function 'void GetMaxValue(T&) [with T = std::vector<int>]'
| GetMaxValue(b); // compiler error
| ^
note: constraints not satisfied
In function 'void GetMaxValue(T&) [with T = std::vector<int>]':
required by the constraints of 'template<class T> requires is_arithmetic_v<T> void GetMaxValue(T&)'
note: the expression 'is_arithmetic_v<T>' evaluated to 'false'
| requires std::is_arithmetic_v<T>
| ~~~~~^~~~~~~~~~~~~~~~~~