Template argument deduction fails when passing enum - c++

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..

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.

CTAD doesn't work with defaulted template arguments?

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}};

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.

C++ 17: Using alias template bug in gcc?

Snippet:
#include <functional>
template <typename T>
struct CallableTrait;
template <typename R, typename... Args>
struct CallableTrait<std::function<R(Args...)>>
{
using ReturnType = R;
};
template <typename Callable>
using CallableTraitT = CallableTrait<decltype(std::function{std::declval<Callable>()})>;
template <typename Callable>
auto test(Callable&&)
{
using CallableInfo = CallableTraitT<Callable>;
static_assert(!std::is_void_v<typename CallableInfo::ReturnType>);
}
int main()
{
test([]() { return 42; });
return 0;
}
Demo
This compiles fine with clang-12.0.0 and MSVC-19.16.27034, but gcc-11.0.0 throws an error:
prog.cc: In instantiation of 'auto test(Callable&&) [with Callable = main()::<lambda()>]':
prog.cc:25:29: required from here
prog.cc:20:25: error: invalid use of incomplete type 'struct CallableTrait<main()::<lambda()> >'
20 | static_assert(!std::is_void_v<typename CallableInfo::ReturnType>);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:5:8: note: declaration of 'struct CallableTrait<main()::<lambda()> >'
5 | struct CallableTrait;
|
Who is right and who isn't?
EDIT:
Bug is tracked here gcc-bugzilla
The issue is tracked here gcc-bugzilla

How to define a contructor for <...auto...>

With the following program, which is an extract from something larger that I'm experimenting with, I get an error message that seems to be related to the constructor for fixed_string
#include <string>
#include <cstring>
template<std::size_t N>
struct fixed_string {
static const constexpr std::size_t size__ = N;
constexpr fixed_string(char const* s) :
buf("") {
for (std::size_t i = 0; i <= N; ++i)
buf[i] = s[i];
}
constexpr operator char const*() const {
return buf;
}
constexpr bool operator==(const char* other) const {
return ::strncmp(buf, other, N) == 0;
}
template<std::size_t M>
constexpr bool compare(const fixed_string<M>& other) const {
return (N == M && ::strncmp(buf, other.buf, N) == 0) ? std::true_type(): std::false_type();
}
char buf[N + 1];
};
template<std::size_t N>
fixed_string(char const (&)[N]) -> fixed_string<N - 1>;
////////////////////////////////////////////
template<fixed_string TARGET_NAME, fixed_string THIS_NAME>
concept NameMatches = (TARGET_NAME.compare(THIS_NAME));
template<fixed_string NAME, typename TYPE>
class Member {
public:
static const constexpr fixed_string name__ { NAME };
public:
template<fixed_string TARGET_NAME>
const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
return member_;
}
protected:
TYPE member_;
};
template<typename ... MEMBERS>
class Container: public MEMBERS... {
};
The error messages are:
../src/test-concepts.cpp:43:35: error: class template argument deduction failed:
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:43:35: error: no matching function for call to ‘fixed_string(fixed_string<...auto...>)’
../src/test-concepts.cpp:8:12: note: candidate: ‘template<long unsigned int N> fixed_string(const char*)-> fixed_string<N>’
8 | constexpr fixed_string(char const* s) :
| ^~~~~~~~~~~~
../src/test-concepts.cpp:8:12: note: template argument deduction/substitution failed:
../src/test-concepts.cpp:43:35: note: couldn’t deduce template parameter ‘N’
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:5:8: note: candidate: ‘template<long unsigned int N> fixed_string(fixed_string<N>)-> fixed_string<N>’
5 | struct fixed_string {
| ^~~~~~~~~~~~
../src/test-concepts.cpp:5:8: note: template argument deduction/substitution failed:
../src/test-concepts.cpp:43:35: note: mismatched types ‘fixed_string<N>’ and ‘fixed_string<...auto...>’
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:28:1: note: candidate: ‘template<long unsigned int N> fixed_string(const char (&)[N])-> fixed_string<(N - 1)>’
28 | fixed_string(char const (&)[N]) -> fixed_string<N - 1>;
| ^~~~~~~~~~~~
../src/test-concepts.cpp:28:1: note: template argument deduction/substitution failed:
../src/test-concepts.cpp:43:35: note: mismatched types ‘const char [N]’ and ‘fixed_string<...auto...>’
43 | const TYPE& get() const requires NameMatches<TARGET_NAME, TYPE::name__> const {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/test-concepts.cpp:43: confused by earlier errors, bailing out
This is part of a project using templates that overloads a given function (get in this case), for which there is only one suitable candidate that fulfils the constraint. The value that the constraint operates on is a name - a string literal - not a const string variable, which is passed as a non-type parameter to an instantiation of the template: something like:
Container<Member<"fred", std::string>, Member<"bert", int>, Member<"alfie", bool>> some_values;
I want to be able to retrieve a value using something like
int result = some_values.get<"bert">();
I have had difficulty finding much information about the "<...auto...>" specialisation of the template. I presume this is an internal representation used by gcc for constant, non-type values.
The error messages point me to the lack of a suitable overloaded constructor for fixed_string. What should this be?
The problem I see is that fixed_string is a template class, not a class.
So, when you define a concept as
template<fixed_string TARGET_NAME, fixed_string THIS_NAME>
concept NameMatches = (TARGET_NAME.compare(THIS_NAME));
you have that fixed_string TARGET_NAME (fixed_string THIS_NAME also) doesn't works because fixed_string isn't a type. I mean: fixed_string<5> is a type, not fixed_string.
I know that you have a deduction guide that, given the literal string, deduce the template parameter for the fixed_string, but remain the problem that the concept should works with elements of different types (fixed_string of different lengths).
I suppose you can solve the problem with auto
template <auto TARGET_NAME, auto THIS_NAME>
concept NameMatches = (TARGET_NAME.compare(THIS_NAME));
but, when you declare Member, you have the same problem: fixed_string NAME doesn't works because fixed_sting (without a length) isn't a type.
template<fixed_string NAME, typename TYPE>
class Member
Unfortunately, if you use auto
template <auto NAME, typename TYPE>
class Member
defining a Member with a literal string argument (Member<"bert", int>, by example), nothing bring "bert" to a fixed_string.
Suggestion: what about a fixed_string without a template argument?