Why not infer template parameter from constructor?
I think it is not valid because the constructor isn't always the only point of entry of the class (I am talking about copy constructor and operator=). So suppose you are using your class like this :
MyClass m(string s);
MyClass *pm;
*pm = m;
I am not sure if it would be so obvious for the parser to know what template type is the MyClass pm;
Not sure if what I said make sense but feel free to add some comment, that's an interesting question.
C++ 17
It is accepted that C++17 will have type deduction from constructor arguments.
Examples:
std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);
Accepted paper.
How to infer template parameter Func from argument of constructor?
The compiler is not able to deduce Func
from your second constructor, so you need to tell it how to determine the Func
class parameter when the first constructor is not viable. You can do that by adding a deduction guide:
template<typename T, typename Step, std::enable_if_t<!std::is_invocable_r_v<T, Step, const T &>, int> = 0>
StepRange(T, T, Step) -> StepRange<T, std::function<T(const T&)>>;
This will deduce Func
to std::function<T(const T&)>
whenever the first constructor is disabled.
Or, if you actually only want the second constructor to be used if the types of all three arguments match, then you can write instead
template<typename T>
StepRange(T, T, T) -> StepRange<T, std::function<T(const T&)>>;
Of course there are some edge cases to be considered in either case.
template argument deduction for constructors
Template argument deduction works for any function, including the constructor. But you can't deduce the class template parameters from arguments passed to the constructor. And no, you can't do it in
C++0x either.
struct X
{
template <class T> X(T x) {}
};
template <class T>
struct Y
{
Y(T y) {}
};
int main()
{
X x(3); //T is deduced to be int. OK in C++03 and C++0x;
Y y(3); //compiler error: missing template argument list. Error in 03 and 0x
}
lock_guard
and thread
aren't class templates. They have constructor templates though.
Constructor can't infer template argument from concept
That's just not the way deduction works in C++20.
For class template argument deduction (CTAD), the client
class template needs to deduce two types (stream_t
and serializer_t
) and the constructor you have only uses one of those types, so the language has no idea where the other one could come from.
You can help this by providing a deduction guide for json_serializer
:
template <stream S>
client(json_serializer<S>) -> client<S, json_serializer<S>>;
And now client c(s);
works because you're telling it where the stream type comes from in this context. This could be generalized to any kind of serializer if you add some associated type for what stream a serializer is associated with.
As a guess:
template <typename S>
using stream_for = decltype(std::declval<S&>().serialize(42));
template <typename Serializer>
client(Serializer) -> client<stream_for<Serializer>, Serializer>;
Which would help if there was a unary concept for serializer
rather than a binary one.
Note that in your concept definition, the requirement:
::stream<stream_t>;
Does not check that stream_t
satisfies the stream
concept. It checks that this is a valid expression. Which it would be regardless of whether the concept is satisfied or not (false
is just as much a valid expression as true
).
This needs to be:
requires ::stream<stream_t>;
Or, even better:
template <class Serializer, class Stream>
concept serializer_for =
stream<Stream>
&& requires (Serializer& s, any_t a) {
{ s.serialize(a) } -> std::same_as<Stream>;
};
Why can't constructors deduce template arguments?
Because nobody has specified how exactly that works. There is a current proposal to the standard committee to make it work. It also lists some of the difficulties:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4471.html
Update: Here's the newest version of the proposal:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0091r0.html
When a compiler can infer a template parameter?
Template parameters can be inferred for function templates when the parameter type can be deduced from the template parameters
So it can be inferred here:
template <typename T>
void f(T t);
template <typename T>
void f(std::vector<T> v);
but not here:
template <typename T>
T f() {
return T();
}
And not in class templates.
So the usual solution to your problem is to create a wrapper function, similar to the standard library function std::make_pair
:
template <class T>
class MyClass {
public:
MyClass(T t) {}
void print(){
std::cout<<"try MyClass"<<std::endl;
}
};
template <typename T>
MyClass<T> MakeMyClass(T t) { return MyClass<T>(t); }
and then call auto a = MakeMyClass(5);
to instantiate the class.
Why compiler said candidate template ignored: couldn't infer template argument 'InputIterator'?
[The code in the question is neither minimal nor reproducible.]
The issue is that parameters of type cls<T>::type
are not deducible. This is the exact pattern you have in:
template <typename InputIterator>
Vector(typename __isInputIterator<InputIterator>::__result, typename __isInputIterator<InputIterator>::__result, const allocator &);
You simply have to reformulate the constructor so that it will be deducible:
template <typename InputIterator,
typename __isInputIterator<InputIterator>::__result* = nullptr>
Vector(InputIterator, InputIterator, const allocator & = allocator{});
This way, the InputIterator is deducible, and its validity is tested by the default parameter. This still won't do what you want, since in your partial code __isInputIterator<InputIterator>::__result
is always defined to something. You want something that is SFINAE, i.e. defined only if this is an input iterator and undefined otherwise. You want something like std::enable_if
:
template <typename InputIterator,
std::enable_if_t<__isInputIterator<InputIterator>::__result::value>* x = nullptr>
Vector(InputIterator, InputIterator, const allocator & = allocator{});
General comments regarding your code:
- You should not use names prefixed with double underscores (
__
) since these names are reserved for the compiler. - The code is overly complicated for what it does. Look at the Stack Overflow question How to check if an arbitrary type is an iterator? for shorter ways to check if a type is an iterator.
This is code with minimum changes that works. It is ugly, and I won't recommend using it. It simply serves to fill up the gaps in the original question:
#include <memory>
#include <type_traits>
struct InputIterator {
constexpr static bool isInputIterator {true};
//...
};
struct __falseType
{
static constexpr bool value{false};
};
struct __trueType
{
static constexpr bool value{true};
};
template <typename, bool>
struct __InputIteratorInferringAuxiliary {
using __type = __falseType;
};
template <typename Iterator>
struct __InputIteratorInferringAuxiliary<Iterator, true> {
using __type = __trueType;
};
struct NotIterator {
static constexpr bool isInputIterator = false;
};
template <typename T>
struct __IteratorTraits {
using iteratorTag = NotIterator;
};
struct RandomAccessIterator {
static constexpr bool isInputIterator = true;
};
template <typename T>
struct __IteratorTraits<T *> {
using sizeType = unsigned long;
using differenceType = long;
using valueType = T;
using reference = valueType &;
using constReference = const valueType &;
using rightValueReference = valueType &&;
using pointer = valueType *;
using constPointer = const valueType *;
using iteratorTag = RandomAccessIterator;//isInputIterator tag in RandomAccessIterator class is true
};
template <typename Iterator>
struct __isInputIterator {
using __result = typename __InputIteratorInferringAuxiliary<Iterator,
__IteratorTraits<Iterator>::iteratorTag::isInputIterator
>::__type;
};
template <class T, class allocator=std::allocator<T>>
class Vector
{
public:
template <typename InputIterator,
std::enable_if_t<__isInputIterator<InputIterator>::__result::value>* x = nullptr>
Vector(InputIterator, InputIterator, const allocator & = allocator{});
T* begin();
T* end();
};
int main()
{
int arr[] {1, 2, 3};
Vector<int> vec(std::begin(arr), std::end(arr));//candidate template ignored: couldn't infer template argument 'InputIterator'
Vector<int> vec2(1,2);//candidate template ignored: couldn't infer template argument 'InputIterator'
}
c++ : using parameter pack in constructors?
With your current code, your instantiation of F<A>
makes the Args
template argument empty, which means the constructor only have the a
argument, not args
.
It seems that you want only the constructor to have a template parameter pack, not the whole class:
template<class T>
class F
{
public:
template<typename ... Args>
F(int a, Args ... args)
{
std::cout << a << std::endl;
T t(args...);
t.print();
}
};
C++ - Use inferred template argument from constructor as template argument throughtout class
No.
But you can make your entire class a template class, and create a factory function that does type deduction to generate your template class.
template<typename C>
class AnyClass {
public:
AnyClass( C* c );
std::vector<C> vec;
};
AnyClass<C> make_any_class( C* c ) {
return AnyClass<C>(c);
};
Related Topics
Using Opencv and Svm With Images
Detecting Superfluous #Includes in C/C++
Normal Mapping Gone Horribly Wrong
Scope Resolution Operator Without a Scope
Why Are Function Pointers and Data Pointers Incompatible in C/C++
Best Way to Extract a Subvector from a Vector
How to Take the Address of a Function Defined in Standard Library
Why Doesn't Delete Set the Pointer to Null
On Which Platforms Does Integer Divide by Zero Trigger a Floating Point Exception
Is There a Replacement For Unistd.H For Windows (Visual C)
Global Memory Management in C++ in Stack or Heap
Why Does C++ Compilation Take So Long
Why Does C++ Allow Us to Surround the Variable Name in Parentheses When Declaring a Variable
How to Combine Hash Values in C++0X
What Is the Purpose of Std::Launder
How to Efficiently Perform Double/Int64 Conversions With Sse/Avx