Variadic template and default value with inheritance - c++

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

Related

C++ constructor behave differently according to input, with help of template

I'm trying to use the template to initialize an object. The code should assign s according to different parameters input, specifically "type". However, I can't get this to work.
#include <iostream>
#include <cmath>
using namespace std;
// different discount
class Strategy {
public:
virtual double GetResult(double) = 0;
};
class SellNormal: public Strategy {
public:
double GetResult(double original) override {
return original;
}
};
class SellDiscount: public Strategy {
double rate;
public:
SellDiscount(double r){
rate = r;
}
double GetResult(double original) override {
return original * rate;
}
};
class SellReturn: public Strategy {
int fulfill;
int reduce;
public:
SellReturn(int f, int r): fulfill(f), reduce(r){}
double GetResult(double original) override {
return original - (int(original) / fulfill) * reduce;
}
};
class SellContext{
Strategy* s;
public:
enum type{
NORMAL,
DISCOUNT,
RETURN
};
template<typename ...Ts>
SellContext(type t, Ts... args){
switch(t){
case NORMAL:
s = new SellNormal();
break;
case DISCOUNT:
// double
s = new SellDiscount(args...);
break;
case RETURN:
// int, int
s = new SellReturn(args...);
break;
}
}
double getResult(double original){
return s->GetResult(original);
}
};
int main(){
auto c1 = new SellContext(SellContext::type::NORMAL);
auto c2 = new SellContext(SellContext::type::DISCOUNT, 0.8);
auto c3 = new SellContext(SellContext::type::RETURN, 300, 100);
cout << c1->getResult(1000);
cout << c2->getResult(1000);
cout << c3->getResult(1000);
}
It's telling me that C++ can't find the appropriate constructor. How should I deal with this elegantly? I know I could achieve this by overloading constructors with different parameters. But isn't that too verbose?
The errors are as below
s
trategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {}]':
strategy_pattern_with_simple_factory.cpp:71:56: required from here
strategy_pattern_with_simple_factory.cpp:57:17: error: no matching function for call to 'SellDiscount::SellDiscount()'
s = new SellDiscount(args...);
^~~~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate: 'SellDiscount::SellDiscount(double)'
SellDiscount(double r){
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(const SellDiscount&)'
class SellDiscount: public Strategy {
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(SellDiscount&&)'
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:61:17: error: no matching function for call to 'SellReturn::SellReturn()'
s = new SellReturn(args...);
^~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate: 'SellReturn::SellReturn(int, int)'
SellReturn(int f, int r): fulfill(f), reduce(r){}
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate expects 2 arguments, 0 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(const SellReturn&)'
class SellReturn: public Strategy {
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(SellReturn&&)'
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {double}]':
strategy_pattern_with_simple_factory.cpp:72:63: required from here
strategy_pattern_with_simple_factory.cpp:61:17: error: no matching function for call to 'SellReturn::SellReturn(double&)'
s = new SellReturn(args...);
^~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate: 'SellReturn::SellReturn(int, int)'
SellReturn(int f, int r): fulfill(f), reduce(r){}
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate expects 2 arguments, 1 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(const SellReturn&)'
class SellReturn: public Strategy {
^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:31:7: note: no known conversion for argument 1 from 'double' to 'const SellReturn&'
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(SellReturn&&)'
strategy_pattern_with_simple_factory.cpp:31:7: note: no known conversion for argument 1 from 'double' to 'SellReturn&&'
strategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {int, int}]':
strategy_pattern_with_simple_factory.cpp:73:66: required from here
strategy_pattern_with_simple_factory.cpp:57:17: error: no matching function for call to 'SellDiscount::SellDiscount(int&, int&)'
s = new SellDiscount(args...);
^~~~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate: 'SellDiscount::SellDiscount(double)'
SellDiscount(double r){
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate expects 1 argument, 2 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(const SellDiscount&)'
class SellDiscount: public Strategy {
^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 2 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(SellDiscount&&)'
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate expects 1 argument, 2 provided
First, Strategy must have a virtual destructor since you'll be destroying instances via base class pointers.
It looks like you are trying to use type as some sort of tag and you can't explicitly supply template parameters to the constructor. That would be a template parameter to the class itself - and SellContext is not a class template, so using tags is a good idea. They do need to be of different types though, so I suggest creating separate tag types and also to make the pointer into a smart pointer.
Example:
#include <memory>
class SellContext{
std::unique_ptr<Strategy> s; // <- smart pointer
public:
// tag types and tag instances:
static constexpr struct NORMAL {} normal_tag{};
static constexpr struct DISCOUNT {} discount_tag{};
static constexpr struct RETURN {} return_tag{};
// The constructors you need. No `switch` needed:
template<class... Args>
SellContext(NORMAL, Args&&... args) :
s(std::make_unique<SellNormal>(std::forward<Args>(args)...)) {}
template<class... Args>
SellContext(DISCOUNT, Args&&... args) :
s(std::make_unique<SellDiscount>(std::forward<Args>(args)...)) {}
template<class... Args>
SellContext(RETURN, Args&&... args) :
s(std::make_unique<SellReturn>(std::forward<Args>(args)...)) {}
double getResult(double original){
return s->GetResult(original);
}
};
Using them would then be done like this:
int main(){
auto c1 = std::make_unique<SellContext>(SellContext::normal_tag);
auto c2 = std::make_unique<SellContext>(SellContext::discount_tag, 0.8);
auto c3 = std::make_unique<SellContext>(SellContext::return_tag, 300, 100);
std::cout << c1->getResult(1000) << '\n';
}
Demo
Using constexpr-if:
#include <type_traits>
class SellContext{
std::unique_ptr<Strategy> s;
public:
static constexpr struct NORMAL {} normal_tag{};
static constexpr struct DISCOUNT {} discount_tag{};
static constexpr struct RETURN {} return_tag{};
template<class Tag, class... Args>
SellContext(Tag, Args&&... args) {
static_assert(std::is_same_v<NORMAL, Tag> ||
std::is_same_v<DISCOUNT, Tag> ||
std::is_same_v<RETURN, Tag>);
if constexpr(std::is_same_v<NORMAL, Tag>)
s = std::make_unique<SellNormal>(std::forward<Args>(args)...);
if constexpr(std::is_same_v<DISCOUNT, Tag>)
s = std::make_unique<SellDiscount>(std::forward<Args>(args)...);
if constexpr(std::is_same_v<RETURN, Tag>)
s = std::make_unique<SellReturn>(std::forward<Args>(args)...);
}
double getResult(double original){
return s->GetResult(original);
}
};
Demo
Since the current constructors of the Strategy objects are all different, you could make it even simpler and remove the tags:
class SellContext{
std::unique_ptr<Strategy> s;
public:
SellContext() : s(std::make_unique<SellNormal>()) {}
SellContext(double x) : s(std::make_unique<SellDiscount>(x)) {}
SellContext(double x, double y) : s(std::make_unique<SellReturn>(x, y)) {}
double getResult(double original){
return s->GetResult(original);
}
};
int main(){
auto c1 = std::make_unique<SellContext>();
auto c2 = std::make_unique<SellContext>(0.8);
auto c3 = std::make_unique<SellContext>(300, 100);
std::cout << c1->getResult(1000) << '\n';
}
Demo

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>&)

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

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.

lambda converted to bool instead of deducing function-pointer-type

I wanted to implement a overload for operator<< that allowed me to call a given function and output the result.
I therefore wrote an overload, but the conversion to bool is selected and when writing a function myself, it would not compile.
EDIT: Know that I do not want to call the lambda,
but instead pass it to the function where it should be called with a default constructed parameter list.
I have appended my code:
#include <iostream>
template<typename T>
void test(T *) {
std::cout << "ptr" << std::endl;
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
template<typename Ret, typename ...Args>
void test(Ret(*el)(Args...)) {
std::cout << "function ptr\n" << el(Args()...) << std::endl;
}
template<typename Char_T, typename Char_Traits, typename Ret, typename ...Args>
std::basic_ostream<Char_T, Char_Traits>& operator<<(
std::basic_ostream<Char_T, Char_Traits> &str, Ret(*el)(Args...)) {
return str << el(Args()...);
}
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test([]{return 5;}); // will not compile
}
I use gcc 7.3.1 with the version flag -std=c++14.
EDIT: Error message:
main.cc: In function ‘int main()’:
main.cc:25:23: error: no matching function for call to ‘test(main()::<lambda()>)’
test([]{return 5;});
^
main.cc:5:6: note: candidate: template<class T> void test(T*)
void test(T *) {
^~~~
main.cc:5:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘T*’ and ‘main()::<lambda()>’
test([]{return 5;});
^
main.cc:9:6: note: candidate: template<class T> void test(bool)
void test(bool) {
^~~~
main.cc:9:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: couldn't deduce template parameter ‘T’
test([]{return 5;});
^
main.cc:13:6: note: candidate: template<class Ret, class ... Args> void test(Ret (*)(Args ...))
void test(Ret(*el)(Args...)) {
^~~~
main.cc:13:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘Ret (*)(Args ...)’ and ‘main()::<lambda()>’
test([]{return 5;});
Your problem here is that Template Argument Deduction is only done on the actual argument passed to test. It's not done on all possible types that the argument could possibly converted to. That might be an infinite set, so that's clearly a no-go.
So, Template Argument Deduction is done on the actual lambda object, which has an unspeakable class type. So the deduction for test(T*) fails as the lambda object is not a pointer. T can't be deduced from test(bool), obviously. Finally, the deduction fails for test(Ret(*el)(Args...)) as the lambda object is not a pointer-to-function either.
There are a few options. You might not even need a template, you could accept a std::function<void(void)> and rely on the fact that it has a templated constructor. Or you could just take a test(T t) argument and call it as t(). T will now deduce to the actual lambda type. The most fancy solution is probably using std::invoke, and accepting a template vararg list.
Even though non-capturing lambdas have an implicit conversion to function pointers, function templates must match exactly for deduction to succeed, no conversions will be performed.
Therefore the easiest fix is to force the conversion with a +
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test(+[]{return 5;});
// ^
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
Template is not needed. In fact you overload functions, not templates. Replace it with
void test(bool) {
std::cout << "bool" << std::endl;
}
Now your sample will compile.

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