C++: How to partially deduce template arguments from make_shared - c++

In order to circumvent the restriction on partially supplied explicit template arguments, I embed the struct from which I want to deduce the class template parameters (Internal) into a second struct (Container).
I would like to enable the user of the code to create e.g. shared pointers of the resulting type. By writing my own create function within the struct, this works just fine.
#include <memory>
/// Container that is used in order to partially specify template arguments
template <int A> struct Container {
/// Contained type, of which the template arguments are deduced.
template <int B> struct Internal {
explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
};
/// Helper function
template <int C>
[[nodiscard]] static auto create(std::integral_constant<int, C> t) noexcept {
return std::make_shared<Container<A>::Internal<C>>(t);
}
};
int main() {
Container<1>::Internal works{std::integral_constant<int, 8>{}};
auto const worksAswell = Container<1>::create(std::integral_constant<int, 8>{});
}
But when I try to use make_shared directly, it fails. I would like to enable the user to use e.g. the std::make_shared function.
int main() {
auto const fails = std::make_shared<Container<1>::Internal>(std::integral_constant<int, 8>{});
}
From what I understand, this fails because I cannot partially specify template arguments, and I am unable to deduce them from the make_shared function if I don't want to specify all template parameters.
main.cc: In function ‘int main()’:
main.cc:21:74: error: no matching function for call to ‘make_shared<1>(std::integral_constant<int, 8>)’
21 | auto const fails = std::make_shared<1>(std::integral_constant<int, 8>{});
| ^
In file included from /usr/include/c++/9.2.0/memory:81,
from /home/juli/main9.cc:1:
/usr/include/c++/9.2.0/bits/shared_ptr.h:714:5: note: candidate: ‘template<class _Tp, class ... _Args> std::shared_ptr<_Tp> std::make_shared(_Args&& ...)’
714 | make_shared(_Args&&... __args)
| ^~~~~~~~~~~
/usr/include/c++/9.2.0/bits/shared_ptr.h:714:5: note: template argument deduction/substitution failed:
Is it possible to enable generator functions like std::make_shared to partially deduce template arguments like that? The entire code can be found here.

If you create your own make_shared that accepts a template template parameter we can use decltype to deduce the resulting type and pass that on to std::make_shared.
#include <memory>
#include <type_traits>
/// Container that is used in order to partially specify template arguments
template <int A> struct Container {
/// Contained type, of which the template arguments are deduced.
template <int B> struct Internal {
explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
};
};
template <template <int> typename partial, typename... Args>
auto make_shared(Args&&... args) {
using target_type = std::remove_pointer_t<decltype(new partial{std::declval<Args>()...})>;
return std::make_shared<target_type>(std::forward<Args>(args)...);
}
using std::make_shared;
int main() {
auto const fails = make_shared<Container<1>::Internal>(std::integral_constant<int, 8>{});
static_assert(std::is_same_v<const std::shared_ptr<Container<1>::Internal<8>>, decltype(fails)>);
}
The only issue here is that our make_shared needs to know the template signature of the expected target.
On the positive side we can add several overloads for different template signatures and we can use one parameter pack.

Related

Pattern matching with variadic templates and default argument

I'm trying to add a default "hidden" setting into a templated class:
template<bool DebugMode=false, typename... Args>
struct A
{
A() {};
};
int main()
{
A<double, double> a;
}
which fails when compile with g++ 8.3.1 and C++17:
error: type/value mismatch at argument 1 in template parameter list for ‘template<bool DebugMode, class ... Args> struct A’
note: expected a constant of type ‘bool’, got ‘double’
Yet I don't understand why g++ can't do any pattern matching in template arguments. Will it be fixed a newer C++ version ?
It's basically the same as with default function arguments: You can only omit parameters from the right. And I don't expect this to change, also because what you want to do can be achieved by adding a layer of indirection:
template<bool DebugMode=false>
struct Wrap {
template <typename ...T> struct A {};
};
template <typename...T> using A = Wrap<>::A<T...>;
int main() {
A<double, double> a;
}
Alternatively:
template <bool DebugMode=false,typename ...T>
struct A_impl {};
template <typename...T>
using A = A_impl<false,T...>;
Though here the default false cannot be really used, for the using you still have to specify it.

What this template syntax "typename = T" mean?

Sometimes I see syntax like this.
template<typename T,typename = int>
int foo(){
//...
}
what part typename = int mean?
Where it can be used?
foo has two template arguments. The first is called T and the second is unnamed and defaults to int.
In your piece of code alone there is no reason to use the second argument. Unnamed template arguments often come up with SFINAE. An example from cppreference:
// primary template handles non-referenceable types:
template<class T, class = void>
struct reference_traits {
using add_lref = T;
using add_rref = T;
};
// specialization recognizes referenceable types:
template<class T>
struct reference_traits<T, std::void_t<T&>> {
using add_lref = T&;
using add_rref = T&&;
};
template<class T>
using add_lvalue_reference_t = typename reference_traits<T>::add_lref;
template<class T>
using add_rvalue_reference_t = typename reference_traits<T>::add_rref;
The only reason for the primary template to have a second argument is that it can be specialized. When possible the more specialized specialization is instantiatied. If this fails (because T& is not valid) then "substitution failure is not an error" (SFINAE) kicks in and the primary template is instantiated instead.
A simpler example of unnamed argument is when you want a template argument merely as a tag to distinguish different instantiations:
template<typename = int>
struct bar {
// ...
};
Even if the implementation of bar does not depend on the template argument you might want to have bar<double> and bar<std::string> be two distinct types.
this is rarely used ...
but this is the default value for the typename but you don't need it here because the compiler itself can overload the function automatically and get the right type for the right arguments you passed !
also it type for what typename ? it's not makes sense here !
it used when you are using nested template ...
I found out in the original reference for C++ :
The template parameter lists of template template parameters can have
their own default arguments, which are only in effect where the
template template parameter itself is in scope:
// class template, with a type template parameter with a default
template<typename T = float> struct B {};
// template template parameter T has a parameter list, which
// consists of one type template parameter with a default
template<template<typename = float> typename T> struct A
{
void f();
void g();
};
// out-of-body member function template definitions
template<template<typename TT> class T>
void A<T>::f()
{
T<> t; // error: TT has no default in scope
}
template<template<typename TT = char> class T>
void A<T>::g()
{
T<> t; // ok: t is T<char>
}
this is the link

Automatic type deduction fails with SFINAE+type traits

This minimal compileable sample seems like a pretty standard setup of SFINAE:
#include <type_traits>
struct AType{};
// Helper type-trait templates for AType
template<typename T> struct isAType{ static const bool value = false; };
template<> struct isAType<AType>{ static const bool value = true; };
template<typename T>
void function( typename std::enable_if<isAType<T>::value, T>::type& t
) {}
int main()
{
AType a1;
// This line, using automatic type deduction, fails to compile:
// function( a1 );
// If you manually specify template parameter, it compiles:
function<AType>( a1 );
}
The error message I get, when function( a1 ); is uncommented, is the following:
main.cpp: In function ‘int main()’:
main.cpp:17:16: error: no matching function for call to ‘function(AType&)’
function( a1 );
^
main.cpp:10:6: note: candidate: template<class T> void function(typename
std::enable_if<isAType<T>::value, T>::type&)
void function( typename std::enable_if<isAType<T>::value, T>::type& t )
{}
^
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:17:16: note: couldn't deduce template parameter ‘T’
function( a1 );
I've seen some posts indicating that "T" is in a nondeduced context. "Nondeduced context" is a new concept to me but enough ink has been spilled elsewhere that I can figure it out. My question here, I guess, is whether my declaration of function can be tweaked in such a way that automatic type deduction will succeed. Is there a canonical way to implement SFINAE with type traits such that automatic type deduction succeeds?
Not all C++ compilers support it, but if yours does this is the cleanest way to do this:
template<bool b>
using sfinae = typename std::enable_if< b, bool >::type;
template<class T,
sfinae<isAType<T>::value> =true
>
void function( T& t )
{
}
in c++14 I wouldn't bother with the sfinae alias, but getting rid of the typename makes it worth it in c++11.
Note that the =true portion is required, but if it was =false it would mean the same thing.
What is going on here is we define a non-type template parameter whose type only exists if the test passes. We then give it a default value.
I find this technique reads the most like the incoming c++17 I mean c++20 I mean c++23 Concepts feature of C++.

C++ Concepts TS: Require nested templated type alias

For a generic library I'm trying to define a concept in terms of having a correct implementation of a traits struct. In particular I want to check that the user has provided all required nested types, static member functions and data members. However, I can't find a way to require a nested templated type(-alias).
I have a declaration of the traits struct
template <typename>
struct trait;
and a specialization for chars
template <>
struct trait<char> {
template <typename>
using type = int;
};
I now define my concept in terms of this trait
template <typename T>
concept bool SatisfiesTrait = requires() {
typename trait<T>; // require the user to have spcialized
// for their type
typename trait<T>::type<long long>; // require the nested template
};
as well as a function requiring a type satisfying this concept
constexpr bool foo(SatisfiesTrait) { return true; }
In my main method I then try to call this function with a char:
int main() {
foo('c');
}
When compiling all this with GCC I get the error message
prog.cc:15:24: error: 'trait<T>::type' is not a type
typename trait<T>::type<long long>;
^~~~
prog.cc: In function 'int main()':
prog.cc:26:11: error: cannot call function 'constexpr bool foo(auto:1) [with auto:1 = char]'
foo('c');
^
prog.cc:18:16: note: constraints not satisfied
constexpr bool foo(SatisfiesTrait) {
^~~
prog.cc:18:16: note: in the expansion of concept 'SatisfiesTrait<auto:1>' template<class T> concept const bool SatisfiesTrait<T> [with T = char]
However, when I change my main function to
int main() {
typename trait<char>::type<long long> v;
(void) v;
foo('c');
}
and comment out the requirement of the nested alias template it compiles just fine. The same problem occurs when the nested type has a non-type template parameter instead of a type parameter.
Am I doing something wrong here or is this a bug in GCCs implementation of the Concepts TS?
The code can also be found on Wandbox.

How to deduce type of `T` from a pointer to member function?

I have a template, more or less like this:
template<typename T,void (T::*F)()>
struct Foo{
/* ... do some stuff with the member function pointer ...*/
//... e.g.
T foo(){
T t;
t.*F;
return t;
};
it works, but I dont like the way I have to instantiate it:
Foo<SomeVeryLongClassName,&SomeVeryLongClassName::AnEvenLongerMemberFunctionName> f;
Is there some way I can make the template deduce T?
I was thinking of a template method that I could call like this:
getFoo(&SomeVeryLongClassName::AnEvenLongerMemberFunctionName);
or, as I will mainly use Foo inside T, that would be just
getFoo(AnEvenLongerMemberFunctionName);
I tried this
#include <iostream>
template <typename T,void (T::*MEMFUN)()>
struct Foo{};
template <typename T,void (T::*MEMFUN)()>
Foo<typename T,typename MEMFUN> getFoo(MEMFUN f){
return Foo<typename T,typename MEMFUN>();
}
struct Bar { void test(){ std::cout << "MUH" << std::endl;} };
int main (){ getFoo(&Bar::test); }
The error messages are actually quite clear, but I dont understand them at all...
templateExample.cpp:9:28: error: wrong number of template arguments (1, should be 2)
Foo<typename T,typename MEMFUN>
^
templateExample.cpp:4:8: error: provided for ‘template<class T, void (T::* MEMFUN)()> struct Foo’
struct Foo{
^
templateExample.cpp:10:7: error: invalid type in declaration before ‘(’ token
getFoo(MEMFUN f){
^
templateExample.cpp:10:7: error: template declaration of ‘int getFoo’
templateExample.cpp:10:15: error: expected ‘)’ before ‘f’
getFoo(MEMFUN f){
^
templateExample.cpp: In function ‘int main()’:
templateExample.cpp:20:20: error: ‘getFoo’ was not declared in this scope
getFoo(&Bar::test);
...why "wrong number of template arguments (1, should be 2)" ?
How can I help the compiler to deduce T when instantiating a Foo ?
Is it possible with only pre-C++11?
PS: this is very close to being a dupe, but I really need to know the type of T and not just call the member function (e.g. I need to create an instance).
In C++17 we have non-type template parameters with deduced types:
template <auto> struct Foo;
template <typename T, void (T::*MF)()> struct Foo<MF> {
// ...
};
Usage: Foo<&X::f>
You can also directly use template <auto X> and either keep using auto inside your template or use decltype(X) to get at the type of the non-type parameter.
Prior to C++17, you could try to perform deduction via some contortions involving helper class templates with member function templates and decltype.
The gory details:
If you define a function template template <typename T, void(T::*MF)()> Foo<T, MF> f(MF);, where Foo is your old-style class template (like template <typename T, void (T::*MF)()> class Foo;), then you can use decltype(f(&X::h)) to deduce the desired type Foo<X, &X::h> without having to repeat X. The price is that you either need to say decltype everywhere, or you wrap that in a macro.