Compilation error passing class template as template parameter - c++

I wrote code like below:
#include <type_traits>
template <typename T>
class myTemplateClass
{
public:
myTemplateClass(T t)
: val{t}
{}
T val;
};
template <template<typename> class TT, typename T>
auto create(T val)
requires std::is_same_v<TT<T>, myTemplateClass<T>>
{
return TT<T>(val);
};
int main()
{
auto result = create<myTemplateClass<int>>(10);
// or
auto result = create(static_cast<int>(10));
}
But both of the create function calls has failed to build.
Error for the 1st call (on MSVC):
error C2672: 'create': no matching overloaded function found
error C3207: 'create': invalid template argument for 'TT', class > template expected
Error for the 2nd call (on MSVC):
error C2672: 'create': no matching overloaded function found
error C2783: 'auto create(T)': could not deduce template argument for 'TT'
I expected the following to compile:
auto result = create(static_cast<int>(10));

create is dependant on 2 template parameters: TT and T.
Therefore one way to do what you want is to mention them explicitly:
//-------------------vvvvvvvvvvvvvvv--vvv-----
auto result = create<myTemplateClass, int>(10);
An altenative way (in which the int is deduced) is mentioned in the comment above (by #songyuanyao):
auto result = create<myTemplateClass>(10);

With
template <template<typename> class TT, typename T>
auto create(T val)
requires std::is_same_v<TT<T>, myTemplateClass<T>>;
Way to call it would be
create<myTemplateClass>(42);
create<myTemplateClass, int>(42);
To allow
auto result = create<myTemplateClass<int>>(10);
You would need:
template <typename TT, typename T>
auto create(T val)
requires std::is_same_v<TT, myTemplateClass<T>>
{
return {val};
}
To allow
auto result = create(10); // myTemplateClass<int>
You would need:
template <typename T>
auto create(T val) -> myTemplateClass<T>
{
return {val};
}
Note
Notice that create (std uses make_xx as std::make_optional/std::make_pair/...) is not really needed with CTAD (C++17), you might write:
auto result = myTemplateClass(10); // myTemplateClass<int>

Related

How do I define a function that takes a variadic class template?

I am trying to define a simple variant-based Result type alias, sort of like a poor man's rust-like Result type
:
namespace detail {
template <typename SuccessType, typename... ErrorTypes>
struct Result {
using type = std::variant<SuccessType, ErrorTypes...>;
};
template <typename... ErrorTypes>
struct Result<void, ErrorTypes...> {
using type = std::variant<std::monostate, ErrorTypes...>;
};
} // namespace detail
template <typename SuccessType, typename... ErrorTypes>
using Result_t = detail::Result<SuccessType, ErrorTypes...>::type;
i.e. a Result_t is just an std::variant where the 0th index is the successful result and the rest are error structs.
I defined this helper method to check if the result is good:
template <typename SuccessType, typename... ErrorTypes>
inline bool Ok(const Result_t<SuccessType, ErrorTypes...>& r) {
return r.index() == 0;
}
But I get a "no matching overloaded function found" when I try to instantiate it:
error C2672: 'Ok': no matching overloaded function found
error C2783: 'bool Ok(const detail::Result<SuccessType,ErrorTypes...>::type &)': could not deduce template argument for 'SuccessType'
struct FileError {};
struct BadJson {};
template <typename T>
using Result = Result_t<T, FileError, BadJson>;
Result<void> GetVoid() { return {}; }
TEST(ConfigFileTest, Result) {
auto res = GetVoid();
EXPECT_EQ(res.index(), 0);
bool ok = Ok(res);
EXPECT_TRUE(ok);
}
What am I doing wrong? If I just have Ok be templated like template <typename T> Ok(const T& r) it works, but makes the function too general.
After expanding the Result_t alias in the function parameter, it looks like this:
template <typename SuccessType, typename... ErrorTypes>
bool Ok(const detail::Result<SuccessType, ErrorTypes...>::type& r) {
return r.index() == 0;
}
The problematic part here is that the template parameters are left of the name resolution operator ::. Everything left of :: is a non-deduced context, meaning that it is not used to deduce template arguments. So since SuccessType and ErrorTypes... appear only in non-deduced context, they cannot be deduced and a call which doesn't explicitly specifies them will fail.
You can see that this rule is necessary, because theoretically any specialization of detail::Result<SuccessType, ErrorTypes...> could have a ::type that matches the arguments type. There is no way that the compiler can check this for every possible combination of types.
Instead of trying to alias types, make Result an actual new type:
template <typename SuccessType, typename... ErrorTypes>
struct Result {
using variant_type = std::variant<SuccessType, ErrorTypes...>;
variant_type variant;
};
template <typename... ErrorTypes>
struct Result<void, ErrorTypes...> {
using variant_type = std::variant<std::monostate, ErrorTypes...>;
variant_type variant;
};
template <typename SuccessType, typename... ErrorTypes>
bool Ok(const Result<SuccessType, ErrorTypes...>& r) {
return r.variant.index() == 0;
}
or something along those lines. If you really want to use the old design using only aliases, then the function should not take the nested alias as argument, but the actual type instead (which is probably not match the intent of the design):
template <typename T, typename... ErrorTypes>
bool Ok(const std::variant<T, ErrorTypes...>& r) {
return r.index() == 0;
}
(I removed the inline on the templates. inline on a function template doesn't really make much sense.)

How do I properly implement "operator()" with "if constexpr" so that it works with std::generate?

I was trying to write a class that will fill a container with random numbers with the type that the container has:
template<typename random_type>
class rand_helper {
private:
std::mt19937 random_engine;
std::uniform_int_distribution<int> int_dist;
std::uniform_real_distribution<double> real_dist;
public:
rand_helper() = delete;
rand_helper(random_type left_b, random_type right_b);
random_type operator()();
};
template<typename random_type>
rand_helper<random_type>::rand_helper(const random_type left_b, const random_type right_b) :random_engine{ std::random_device{}()} {
if constexpr (std::is_same_v<random_type, double>)
real_dist(left_b, right_b );
else
int_dist( left_b, right_b );
}
template<typename random_type>
random_type rand_helper<random_type>::operator()() {
if constexpr (std::is_same_v<random_type, double>)
return real_dist(random_engine);
else
return int_dist(random_engine);
}
But here an error occurs somewhere, because when I call std::generate,then I get a lot of errors:
template<typename T,typename vec_type = typename T::value_type>
void fill_contain(T& container,vec_type left_b=vec_type(0), vec_type right_b= vec_type(100)) {
std::generate(std::begin(container),std::end(container), rand_helper<vec_type>(left_b ,right_b));
}
I thought it might be because of if constexpr but if just leave:
template<typename random_type>
random_type rand_helper<random_type>::operator()() {
return int_dist(random_engine);
}
then the same errors are still returned.
Here is the list of errors I get:
Error C2825 '_Urng': must be a class or namespace when followed by '::'
Error C2510 '_Urng' : left of '::' must be a class / struct / union
Error C2061 syntax error : identifier 'result_type'
Error C2065 '_Ty1' : undeclared identifier
Error C2923 'std::conditional_t' : '_Ty1' is not a valid template type argument for parameter '_Ty2'
The function call goes like this:
std::vector<int> for_sort;
fill_contain(for_sort);
Your code just doesn't compile, regardless of the if constexpr. The reason you may not be getting a compilation error with just the template class is that, well, it's a template, so no actual instance of anything gets compiled. If you add:
template class rand_helper<int>;
which forces an instantiation for a random_type of int, you'll get plenty of compilation error output.
Specifically, you'll be told that you need a pseudo-randomness generator to construct a uniform_int_distribution<int>.
Regardless of the above - you can use something like:
template <typename T>
using uniform_distribution = std::conditional_t<
std::is_integral_v<T>,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>
>;
to only have just one distribution member. And in that case, you might not even need your helper class.
To avoid instantiating the std::uniform_real_distribution template class with a non-floating-point type and getting a potentially confusing diagnostic, I'd prefer to use template specializations like this rather than std::conditional_t:
namespace detail
{
template <typename T, typename AlwaysVoid = void>
struct uniform_distribution_impl
{
static_assert(sizeof(T) == 0, "T must be integral or floating point");
};
template <typename T>
struct uniform_distribution_impl<
T, std::enable_if_t<std::is_integral_v<T>>>
{
using type = std::uniform_int_distribution<T>;
};
template <typename T>
struct uniform_distribution_impl<
T, std::enable_if_t<std::is_floating_point_v<T>>>
{
using type = std::uniform_real_distribution<T>;
};
}
template <typename T>
using uniform_distribution = typename detail::uniform_distribution_impl<T>::type;

passing lambda to void specified template fails

i simplified the problem as much as i could so here is the function in question:
class Test
{
public:
template<class T>
void ExecuteFunction(std::function<void(T)> f)
{
}
};
if i call the function with int-typing everything works fine, however, if i call it with a void-typed lambda it doesn't compile anymore.
Test test;
test.ExecuteFunction<void>( // doesn't compile
[](void)->void
{
int i = 5;
});
test.ExecuteFunction<int>( // this compiles
[](int)->void
{
int i = 5;
});
Compiler errors:
Error C2672 'Test::ExecuteFunction': no matching overloaded function found
Error C2770 invalid explicit template argument(s) for 'void Test::ExecuteFunction(std::function<void(P)>)'
Error (active) no instance of function template "Test::ExecuteFunction" matches the argument list
is there a way around this? how would someone specify the template so that both calls work?
Sure, void in parentheses is but a vintage C-style sugar. You'll have to specialize your template:
template<> void Test::ExecuteFunction<void>(std::function<void()> f) {}
If that does not compile, well, you can use a helper template to encapsulate the type-selection:
#include <iostream>
#include <functional>
template<class T> struct callable {
using type = std::function<void(T)>;
};
template<class T> using callable_t =
typename callable<T>::type;
template<> struct callable<void> {
using type = std::function<void()>;
};
class Test
{
public:
template<class T>
void ExecuteFunction(callable_t<T> f) {}
};
int main() {
Test test;
test.ExecuteFunction<void>( // does compile
[](void)->void {});
test.ExecuteFunction<int>( // this compiles
[](int)->void {});
}
But be aware that this way you'll have to also do something to the arguments passing (in your example, a generic case's argument is unary yet specialization for void expects a nullary function object).
You can add an overload to the class like this:
// as before:
template<class T>
void ExecuteFunction(std::function<void(T)> f) {}
// new overload (not a template):
void ExecuteFunction(std::function<void()> f) {}
As you can't use type deduction anyhow, you can now explicitly call this function by not specifying any template parameter as follows.
Test test;
test.ExecuteFunction(
[](void)->void
{
int i = 5;
});
Is too late to play?
I propose another solution based on a custom type trait (with a specialization for void) that, given a T type, define the correct std::function type; i mean
template <typename T>
struct getFuncType
{ using type = std::function<void(T)>; };
template <>
struct getFuncType<void>
{ using type = std::function<void()>; };
This way your ExecuteFunction() simply become
template <typename T>
void ExecuteFunction (typename getFuncType<T>::type f)
{
}
If you want simplify a little the use of getFuncType, you can add a using helper to extract the type
template <typename T>
using getFuncType_t = typename getFuncType<T>::type;
so the ExecuteFunction() can be simplified as follows
template <typename T>
void ExecuteFunction (getFuncType_t<T> f)
{
}

Confused by template argument deduction

I've created the following struct:
template<class T>
struct not_equals
{
not_equals(T d):data(d){};
bool operator()(T const & in)
{
return data != in;
}
T data;
};
My expectation was that since I need to pass some value of concrete type d to the constructor, template argument T will be deduced from type of d.
However, this does not happens.
not_equals('0'); // fails with 'missing template arguments'
char zero = '0';
not_equals(zero); // same as above
not_equals<char>('0'); // compiles without errors
What is the reason for compiler not recognizing type of template argument?
c++17 will allow class template deduction
Until then, you can create a "make" function:
template <class T> auto make_not_equals(const T& d) -> not_equals<T>
{
return {d};
}
auto z = make_not_equals('0');
This is the C++03 version of the make function:
template <class T> not_equals<T> make_not_equals(const T& d)
{
return not_equals<T>(d);
}
Unfortunately when you declare a variable you need to spell the whole type with the template because of missing auto feature, but the make function can still be helpful in a deduced context, for instance for a parameter:
template <class T> void foo(not_equals<T> ne);
void test()
{
foo(make_not_equals('0'));
}

Template argument type deduction from within the class definition

Is it possible to use class template argument deduction for a class C from within the definition of one of C's member functions? ...or am I forced to write my make_c helper class like in C++03?
Consider this minimized and simplified scenario that builds a chain of arbitrary function objects:
template <typename F>
struct node;
template <typename FFwd>
node(FFwd&&) -> node<std::decay_t<FFwd>>;
The node class stores a function object which is initialized via perfect-forwarding. I need a deduction guide here to decay the type of the function
object.
template <typename F>
struct node
{
F _f;
template <typename FFwd>
node(FFwd&& f) : _f{std::forward<FFwd>(f)}
{
}
template <typename FThen>
auto then(FThen&& f_then)
{
return node{[f_then = std::move(f_then)]
{
return f_then();
}};
}
};
Afterwards, I define node's constructor and a .then continuation member function that returns a new node (its implementation is nonsensical to minimize the size of the example). If I attempt to invoke .then...
auto f = node{[]{ return 0; }}.then([]{ return 0; });
...I get an unexpected compilation error:
prog.cc: In instantiation of 'node<F>::node(FFwd&&) [with FFwd = node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]::<lambda()>; F = main()::<lambda()>]':
prog.cc:27:22: required from 'auto node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]'
prog.cc:35:56: required from here
prog.cc:17:46: error: no matching function for call to 'main()::<lambda()>::__lambda1(<brace-enclosed initializer list>)'
node(FFwd&& f) : _f{std::forward<FFwd>(f)}
^
prog.cc:35:20: note: candidate: 'constexpr main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
auto f = node{[]{ return 0; }}.then([]{ return 0; });
^
live example on wandbox
This happens because inside the body of node<F>::then, node{...} creates an instance with the type of *this - it doesn't trigger argument type deduction. I am therefore forced to write:
template <typename FThen>
auto then(FThen&& f_then)
{
auto l = [f_then = std::move(f_then)]{ return f_then(); };
return node<std::decay_t<decltype(l)>>{std::move(l)};
}
live example on wandbox
...which defeats the whole purpose of the deduction guide.
Is there a way I can use class template argument deduction here without introducing code repetition or a make_node function?
The name lookup for node found the injected-class-name, from which deduction is not performed. (Performing deduction in this case would have been a backward compatibility break.)
If you want deduction, qualify the name so that you find the namespace member.
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{[f_then = std::move(f_then)]
// ^^
{
return f_then();
}};
}
Also, a cleaner way to write the guide is
template <typename F>
node(F) -> node<F>;
I found a possible solution, but it does require an external implementation of a make function.
Good news: it doesn't have to be make_node - it can work with any type T that supports class template argument deduction.
template <template <typename...> class T, typename... Ts>
auto make(Ts&&... xs)
noexcept(noexcept(T{std::forward<Ts>(xs)...}))
-> decltype(T{std::forward<Ts>(xs)...})
{
return T{std::forward<Ts>(xs)...};
}
Usage:
template <typename FThen>
auto then(FThen&& f_then)
{
return make<node>([f_then = std::move(f_then)]
{
return f_then();
});
}