Why is there a difference between brace and parentheses initialization here? - c++

I'm trying to recreate a simple example from this article on the common "overloaded lambda" trick to create an overload set that can be used with std::visit or other similar facilities. My simplified example is:
#include <iostream>
#include <vector>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; // (1)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; // (2)
int main() {
overloaded os(
[](int i) { std::cout << "int: " << i << std::endl; },
[](const char *str) { std::cout << "str: " << str << std::endl; }
);
os(1);
os("Hello world!");
return 0;
}
This does not compile.
<source>: In function 'int main()':
<source>:12:5: error: no matching function for call to 'overloaded<main()::<lambda(int)>, main()::<lambda(const char*)> >::overloaded(main()::<lambda(int)>, main()::<lambda(const char*)>)'
12 | );
| ^
<source>:4:30: note: candidate: 'constexpr overloaded<main()::<lambda(int)>, main()::<lambda(const char*)> >::overloaded(const overloaded<main()::<lambda(int)>, main()::<lambda(const char*)> >&)'
4 | template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; // (1)
| ^~~~~~~~~~
<source>:4:30: note: candidate expects 1 argument, 2 provided
<source>:4:30: note: candidate: 'constexpr overloaded<main()::<lambda(int)>, main()::<lambda(const char*)> >::overloaded(overloaded<main()::<lambda(int)>, main()::<lambda(const char*)> >&&)'
<source>:4:30: note: candidate expects 1 argument, 2 provided
If I change the initialization of overloaded os to use brace initialization, then it works. Can anyone explain the distinction here?

Here's a reduced example without any templates to deal with:
struct A { };
struct B { };
struct C : A, B { };
C x(A{}, B{}); // error
C y{A{}, B{}}; // ok
The issue is: C is an aggregate, so you can use aggregate initialization to initialize its components. This is why y works. But C is an aggregate, it doesn't have constructors, which is what the initialization of x is trying to do. There's no such matching constructor, hence it fails. Note that in C++20, x will also work, because we will be able to perform aggregate initialization with parentheses.
The way to get the declaration of x to compile is to add a constructor:
struct C : A, B {
C(A a, B b) : A(a), B(b) { }
};
Or, for the original problem:
template<class... Ts>
struct overloaded : Ts... {
overloaded(Ts... ts) : Ts(std::move(ts))... { } // <==
using Ts::operator()...;
};
Or just stick with aggregate initialization, since that's more explicitly what we're doing here.

Related

Callback function in a templated function accepting variable argument type using `switch` statement

I am trying to call a callback function from within a templated function. But the arguments for the callback function depend on switch statement.
Here's the working code which explains what I intend to do using a toy example.
#include <vector>
struct A {};
struct B {};
struct C {};
struct D {};
enum class StructType
{
A,
B,
C,
D
};
std::vector<A> vec_A;
std::vector<B> vec_B;
std::vector<C> vec_C;
std::vector<D> vec_D;
template <template <class> class Container, class ValueType>
void process(const StructType& struct_type)
{
auto callback = [&](Container<ValueType>& v) {};
switch(struct_type)
{
case StructType::A:
callback(vec_A);
break;
case StructType::B:
callback(vec_B);
break;
case StructType::C:
callback(vec_C);
break;
case StructType::D:
callback(vec_D);
break;
}
}
int main()
{
process<std::vector, A>(StructType::A);
}
On compilation, I get the following error:
$ g++ template.cpp
template.cpp: In instantiation of ‘void process(const StructType&) [with Iterator = std::vector; ValueType = A]’:
template.cpp:47:26: required from here
template.cpp:33:15: error: no match for call to ‘(process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>) (std::vector<B>&)’
33 | callback(vec_B);
| ~~~~~~~~^~~~~~~
template.cpp:25:19: note: candidate: ‘process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>’
25 | auto callback = [&](Iterator<ValueType>& v) {};
| ^
template.cpp:25:19: note: no known conversion for argument 1 from ‘std::vector<B>’ to ‘std::vector<A>&’
template.cpp:36:15: error: no match for call to ‘(process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>) (std::vector<C>&)’
36 | callback(vec_C);
| ~~~~~~~~^~~~~~~
template.cpp:25:19: note: candidate: ‘process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>’
25 | auto callback = [&](Iterator<ValueType>& v) {};
| ^
template.cpp:25:19: note: no known conversion for argument 1 from ‘std::vector<C>’ to ‘std::vector<A>&’
template.cpp:39:15: error: no match for call to ‘(process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>) (std::vector<D>&)’
39 | callback(vec_D);
| ~~~~~~~~^~~~~~~
template.cpp:25:19: note: candidate: ‘process<std::vector, A>(const StructType&)::<lambda(std::vector<A>&)>’
25 | auto callback = [&](Iterator<ValueType>& v) {};
| ^
template.cpp:25:19: note: no known conversion for argument 1 from ‘std::vector<D>’ to ‘std::vector<A>&’
My g++ version is: 11.1.0.
I can understand the error but just not sure how to fix this.
Any help is greatly appreciated. Thanks.
EDIT 1: Iterator -> Container for template type made more sense as suggested by #Ted Lyngmo.
As your errors say, there are no matches for callback functors you haven't created.
I suggest removing the runtime argument to process and using the information you have in the function template parameters only.
Example:
#include <vector>
#include <type_traits>
struct A {};
struct B {};
struct C {};
struct D {};
// enum class StructType {A, B, C, D }; // perhaps not needed anymore
std::vector<A> vec_A;
std::vector<B> vec_B;
std::vector<C> vec_C;
std::vector<D> vec_D;
template<class T> auto& get_vec();
template<> auto& get_vec<std::vector<A>>() { return vec_A; }
template<> auto& get_vec<std::vector<B>>() { return vec_B; }
template<> auto& get_vec<std::vector<C>>() { return vec_C; }
template<> auto& get_vec<std::vector<D>>() { return vec_D; }
template <template <class, class...> class Container, class ValueType>
void process() {
using container_type = Container<ValueType>;
auto callback = [&](container_type&) {};
callback(get_vec<container_type>());
}
int main() {
process<std::vector, A>();
}

std::enable_if to conditionally disable a template constructor

I am trying to get a simple example to work to understand how to use std::enable_if, here is the problem:
I am reading the textbook C++ Templates The Complete Guide by David Vandevoorde, Nicolai M.Josuttis, Chapter 6, Section 5.
This chapter mentions: "std::enable_if to prevent being able to copy objects of a class template C<> if the template parameter is an integral type", and its following code:
template <typename T> class C {
public:
// user-define the predefined copy constructor as deleted (with conversion to
// volatile to enable better matches)
C(C const volatile &) = delete;
// if T is not integral type, provide copy constructor template with better match:
template <typename U,
typename = std::enable_if_t<!std::is_integral<U>::value>>
C(C<U> const &) {
std::cout << "tmpl copy constructor" << std::endl;
}
};
My question is, how should the above code be called and used?
for example, I tried:
C<int> c_int;
std::string s = "sname";
C<std::string> c_string1(std::string);
C<std::string> c_string2(c_string1);
But give me compile error:
specialmember3.cc:22:39: error: no matching function for call to ‘C<std::__cxx11::basic_string<char> >::C(C<std::__cxx11::basic_string<char> > (&)(std::string))’
22 | C<std::string> c_string2(c_string1);
| ^
specialmember3.cc:14:3: note: candidate: ‘template<class U, class> C<T>::C(const C<U>&)’
14 | C(C<U> const &) {
| ^
specialmember3.cc:14:3: note: template argument deduction/substitution failed:
specialmember3.cc:22:39: note: mismatched types ‘const C<U>’ and ‘C<std::__cxx11::basic_string<char> >(std::string)’ {aka ‘C<std::__cxx11::basic_string<char> >(std::__cxx11::basic_string<char>)’}
22 | C<std::string> c_string2(c_string1);
| ^
specialmember3.cc:9:3: note: candidate: ‘constexpr C<T>::C() [with T = std::__cxx11::basic_string<char>]’
9 | C() = default;
| ^
specialmember3.cc:9:3: note: candidate expects 0 arguments, 1 provided
Can someone please give me some hints or code guidance on how to use above template constructor?
You have declared c_string1 as a function. I think you meant this
C<std::string> c_string1;
C<std::string> c_string2(c_string1);
That class, as it is defined, is somewhat useless.
a) You can't default-initialize it, default constructor is removed. C<int> c_int; is ill-formed.
b) You can't create it from value, e.g. C<std::string> c_string1(some_str);, because that constructor does not exist.
Essentially you can copy it, but you cannot create it, which is a nonsense. It breaks rule of 3/5/0.
template <typename T>
class C {
public:
C() /*Initialization here */ {}
C(const T& val) /*Initialization here */ {
std::cout << "tmpl copy constructor: " << val << std::endl;
}
// user-define the predefined copy constructor as deleted (with conversion to
// volatile to enable better matches)
C(C const volatile &) = delete;
// if T is not integral type, provide copy constructor template with better match:
template <typename U,
typename = std::enable_if_t<!std::is_integral<U>::value>>
C(C<U> const &) {
std::cout << "tmpl copy constructor" << std::endl;
}
};
In that case those would be legal:
C<int> c_int;
std::string s = "sname";
C<std::string> c_string1(s);
C<std::string> c_string2(c_string1);
THe copy template defined disallows copying of C if T is integral, so
C<int> c_int2 (c_int); // use of deleted function 'C<T>::C(const volatile C<T>&)

Difference between g++ and clang++ with enable_if

I want to write a function that returns an instance of a type T but behaves differently depending on how T can be constructed. Say I have structs like these
#include <type_traits>
#include <iostream>
struct A {};
struct B {};
struct C {
C(A a) {
std::cout << "C" << std::endl;
}
};
I want to create Cs by giving them an A. I have a struct like so that uses enable_if to choose one of two functions:
struct E {
template< bool condition = std::is_constructible<C, A>::value,std::enable_if_t<condition,int> = 0>
C get() {
return C{A{}};
}
template< bool condition = std::is_constructible<C, B>::value,std::enable_if_t<condition,bool> = false>
C get() {
return C{B{}};
}
};
This compiles fine with g++82 (and I think also g++9), but clang9 gives me the error
$ clang++ --std=c++17 main.cpp
main.cpp:26:12: error: no matching constructor for initialization of 'C'
return C{B{}};
^~~~~~
main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const C' for 1st argument
struct C {
^
main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'C' for 1st argument
struct C {
^
main.cpp:7:3: note: candidate constructor not viable: no known conversion from 'B' to 'A' for 1st argument
C(A a) {
^
1 error generated.
even though the enable_if should hide that function. (I call E e; auto c = e.get();). If I don't hardcode C but instead use a template to pass in C it works in both compilers.
template<typename T>
struct F {
template< bool condition = std::is_constructible<T, A>::value,std::enable_if_t<condition,int> = 0>
T get() {
return T{A{}};
}
template< bool condition = std::is_constructible<T, B>::value,std::enable_if_t<condition,bool> = false>
T get() {
return T{B{}};
}
};
I don't understand why clang apparently typechecks the body of the function even though the function should be disabled by enable_if.
Both compiler are right,
http://eel.is/c++draft/temp.res#8.1
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
(8.1)
- no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
[..]
(8.4)
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
return C{B{}}; is not template dependent, and wrong. clang is fine by diagnosing the issue.
Since you seem to have access to compilers supporting c++17 you could use if constexpr instead of enable_if to accomplish what you want.
#include <iostream>
#include <type_traits>
struct A {};
struct B {};
struct C {
explicit C(A a) {
std::cout << "C" << std::endl;
}
};
template<typename T>
struct False : std::false_type {};
struct E {
template<typename T = void>
C get() const {
if constexpr (std::is_constructible_v<C, A>) {
return C{A{}};
} else if constexpr (std::is_constructible_v<C, B>) {
return C{B{}};
} else {
static_assert(False<T>::value, "Error");
}
}
};
int main() {
const auto C{E{}.get()};
return 0;
}

Variadic template and default value with inheritance

#include <iostream>
struct A {
int a;
std::string b;
A(int a_, std::string b_) : a(a_), b(b_) { std::cout << a << b << std::endl; }
};
struct B : public A {
static const int VERSION=2;
float c;
template<typename ... ARGS>
B(float c_, int v=VERSION, ARGS&&... args) : A(v, std::forward<ARGS>(args)...), c(c_) { std::cout << c << std::endl; }
};
int main() {
B(3.14, "abc");
}
Hi all, the compiler gives gives me template argument deduction/substitution failed error. How can I use a default value with a variadic template?
variadic.cpp: In function ‘int main()’:
variadic.cpp:18:15: error: no matching function for call to ‘B::B(double, const char [4])’
B(3.14, "abc");
^
variadic.cpp:14:2: note: candidate: template<class ... ARGS> B::B(float, int, ARGS&& ...)
B(float c_, int v=VERSION, ARGS&&... args) : A(v, std::forward<ARGS>(args)...), c(c_) { std::cout << c << std::endl; }
^
variadic.cpp:14:2: note: template argument deduction/substitution failed:
variadic.cpp:18:15: note: cannot convert ‘"abc"’ (type ‘const char [4]’) to type ‘int’
B(3.14, "abc");
^
variadic.cpp:9:8: note: candidate: B::B(const B&)
struct B : public A {
^
variadic.cpp:9:8: note: candidate expects 1 argument, 2 provided
variadic.cpp:9:8: note: candidate: B::B(B&&)
variadic.cpp:9:8: note: candidate expects 1 argument, 2 provided
The problem here is that your constructor can be called with one, two or more arguments.
If you call it with one argument, the second argument is defaulted.
If you provide two or more arguments, the provided default argument is not used. Your second argument is used, and it has to match the type of the second parameter.
Note that in general, you can achieve similar results by overloading a function instead of providing default arguments. In this case, I suspect that would give you the results you intend, but that's me guessing at your intents.
The problem is if a function/method parameter has a default value, all following parameters must have a default value.
So
template<typename ... ARGS>
B(float c_, int v=VERSION, ARGS&&... args)
: A(v, std::forward<ARGS>(args)...), c(c_)
{ std::cout << c << std::endl; }
is wrong because there aren't default values for args.
Or better: you can write the following signature
B(float c_, int v=VERSION, ARGS&&... args)
but the default value for v is used only if you pass to the constructor only a value (c_) because args... is empty so v is the last parameter.
But if you want a some args..., the default value for v is ignored because, otherwise, the compiler can't know if a second integer parameter is the not-default value for v or the first args...

Call type's tagged constructor if available, default otherwise

I'm trying to build up some code that wants to declare a local variable (say of type test, as shown below). Construction of that local variable should use a constructor that takes a special Tag argument if such a constructor exists, or the default constructor otherwise.
What we've been able to come up with is as follows, where we specialize to construct either a void argument or a Tag argument, but compilers don't like that:
#include <iostream>
using std::cout;
struct Tag { };
template <bool z>
struct helper {
using type = void;
};
template <>
struct helper<true> {
using type = Tag;
};
template <bool z>
static typename helper<z>::type get_arg() {
return typename helper<z>::type();
}
struct test {
test(void) { cout << "test(void)\n"; }
test(Tag x) { cout << "test(Tag)\n"; }
test(const test&) = delete;
test(test&&) = delete;
};
template <typename T>
void try_construct() {
// we would be selecting from one of these by template metaprogramming
T a{typename helper<false>::type()};
T b{typename helper<true>::type()};
T c{get_arg<false>()};
T d{get_arg<true>()};
// Then do stuff with the suitably-constructed instance of T
// . . .
}
int main(void) {
try_construct<test>();
return 0;
}
Compiler output:
$ g++ -std=c++11 -c foo.cpp
foo.cpp: In instantiation of 'void try_construct() [with T = test]':
foo.cpp:38:23: required from here
foo.cpp:30:37: error: no matching function for call to 'test::test(<brace-enclosed initializer list>)'
T a{typename helper<false>::type()};
^
foo.cpp:30:37: note: candidates are:
foo.cpp:22:3: note: test::test(Tag)
test(Tag x) { cout << "test(Tag)\n"; }
^
foo.cpp:22:3: note: no known conversion for argument 1 from 'void' to 'Tag'
foo.cpp:21:3: note: test::test()
test(void) { cout << "test(void)\n"; }
^
foo.cpp:21:3: note: candidate expects 0 arguments, 1 provided
foo.cpp:33:23: error: no matching function for call to 'test::test(<brace-enclosed initializer list>)'
T c{get_arg<false>()};
^
foo.cpp:33:23: note: candidates are:
foo.cpp:22:3: note: test::test(Tag)
test(Tag x) { cout << "test(Tag)\n"; }
^
foo.cpp:22:3: note: no known conversion for argument 1 from 'helper<false>::type {aka void}' to 'Tag'
foo.cpp:21:3: note: test::test()
test(void) { cout << "test(void)\n"; }
^
foo.cpp:21:3: note: candidate expects 0 arguments, 1 provided
We know how to test on the presence of the constructor, so I've left that our of the example. If that does end up being relevant in a solution taking a different approach, feel free to go that route.
Our ultimate goal is to require one of the default constructor or the Tag constructor, and neither of the copy or move constructors.
namespace details {
template<class T>
T maybe_tag_construct(std::true_type) {
return T(Tag{});
}
template<class T>
T maybe_tag_construct(std::false_type) {
return T();
}
}
template<class T>
T maybe_tag_construct() {
return details::maybe_tag_construct<T>( std::is_constructible<T, Tag>{} );
}
now auto t =maybe_tag_construct<test>(); constructs test from Tag iff it works.
It also does elided move construction before c++17, and in c++17 no move construction occurs.
In order to pass an instance of void around, you need the "regular void" proposal, which is on track for c++2a last I checked.
I think something along these lines works:
#include <type_traits>
template <typename T, bool B = std::is_constructible<Tag, T>> struct H;
template <typename T>
struct H<T, false> {
T t;
H() : t() {}
};
template <typename T>
struct H<T, true> {
T t;
H() : t(Tag) {}
};
try_construct() {
H<T> h;
h.t;
}