template parameter inference for std pair [duplicate] - c++

This question already has answers here:
Template argument deduction for implicit pair
(2 answers)
Closed 1 year ago.
In the code below, X x1{{100,5.55}, "abc"} fails to compile ("candidate template ignored: couldn't infer template argument 'T1'").
I believe that this is because, template deduction does not consider implicit conversions, so the compiler does not treat the first argument as a std::pair.
Is there any way to make the X x1{{100,5.55}, "abc"} syntax work? I want to avoid explicit use of std::pair in the argument, because I am refactoring a large code base where this expression is used in many places.
(In the current code, the constructor is not templated, because it uses std::variant to create an omnibus class that handles multiple types with implicit constructors for int, double, std::string, etc. I am trying to replace this class with a templated constructor that preserves the underlying data types, to improve type safety.)
#include <utility>
struct X
{
template <class T1, class T2> X(std::pair<T1,T2>, const char*){}
};
int main()
{
X x{std::pair{100,5.55}, "abc"}; // OK
// X x1{{100,5.55}, "abc"}; // Compile error
}

I don't have a perfect solution, but if I am understanding your constraints well enough, it is not a big deal if new code needs to explicitly use std::pair{}, but old code has to remain compatible.
What about introducing a simple compatibility struct and (ab)using aggregate initialization to provide a constructor overload?
#include <utility>
#include <iostream>
struct X
{
template <class T1, class T2> X(std::pair<T1,T2>, const char*)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
struct LegacyPair
{
int a;
double b;
};
X(LegacyPair p, const char* text) :
X(std::pair{p.a, p.b}, text)
{}
};
int main()
{
X x1{std::pair{100,5.55}, "abc"};
X x2{{100,5.55}, "abc"};
//X x3{{3.14,5}, "abc"}; // error
X x4{std::pair{3.14,5}, "abc"};
}
This outputs:
X::X(std::pair<_T1, _T2>, const char*) [with T1 = int; T2 = double] for x1
X::X(std::pair<_T1, _T2>, const char*) [with T1 = int; T2 = double] for x2
An error for x3 (better than accidentally constructing a std::pair<int, double>). Because {} is aggregate initialization here, implicit conversion is not allowed, so this overload gets discarded. Which is good!
X::X(std::pair<_T1, _T2>, const char*) [with T1 = double; T2 = int] for x4

Related

Constraints not satisfied for template template concept requiring static template method

I'm trying to implement Functor and various other category-theoretic concepts using C++ concepts, but am getting compile errors:
http://coliru.stacked-crooked.com/a/e8b6eb387229bddf
Here's my full code (I know that requiring fmap<int, int> does not verify fmap for any two types, and I plan to change it to fmap<int, std::string> or something to achieve a slightly stronger test -- or instead, possibly alter the Functor concept so that it takes in addition to F, two types T and U and verifies the existence of fmap<T, U>, but that's all after I figure out how to fix the error that I'm getting):
#include <functional>
#include <iostream>
#include <vector>
// empty Functor_Impl struct - specialize for each functor
template<template<class> class F> struct Functor_Impl {};
// std::vector Functor implementation
template<>
struct Functor_Impl<std::vector> {
template<class T, class U>
static std::vector<U> fmap(std::vector<T> x, std::function<U(T)> f) {
std::vector<U> out;
out.reserve(x.size());
for (int i = 0; i < x.size(); i++) {
out.push_back(f(x[i]));
}
return out;
}
};
// Functor concept requires Functor_Impl<F> to have fmap
template<template<class> class F>
concept bool Functor = requires(F<int> x) {
{Functor_Impl<F>::template fmap<int, int>(x)} -> F<int>;
};
// Test function using constraint.
template<template<class> class F, class T>
requires Functor<F>
F<T> mult_by_2(F<T> a) {
return Functor_Impl<F>::template fmap<T, T>(a, [](T x) {
return x * 2;
});
}
int main() {
std::vector<int> x = {1, 2, 3};
std::vector<int> x2 = mult_by_2(x);
for (int i = 0; i < x2.size(); i++) {
std::cout << x2[i] << std::endl;
}
}
And the compile error:
lol#foldingmachinebox:~/p/website-editor$ g++ foo.cpp -std=c++17 -fconcepts -o foo
foo.cpp: In function ‘int main()’:
foo.cpp:39:38: error: cannot call function ‘F<T> mult_by_2(F<T>) [with F = std::vector; T = int]’
std::vector<int> x2 = mult_by_2(x);
^
foo.cpp:31:6: note: constraints not satisfied
F<T> mult_by_2(F<T> a) {
^~~~~~~~~
foo.cpp:24:14: note: within ‘template<template<class> class F> concept const bool Functor<F> [with F = std::vector]’
concept bool Functor = requires(F<int> x) {
^~~~~~~
foo.cpp:24:14: note: with ‘std::vector<int> x’
foo.cpp:24:14: note: the required expression ‘Functor_Impl<F>::fmap<int, int>(x)’ would be ill-formed
I'm guessing that my syntax for the concept itself is wrong - that it's treating a variable as a function, or vice versa, since I'm not very familiar with the concept syntax, and in addition some of the example code on cppreference.com does not compile under GCC's implementation (e.g. concept EqualityComparable does not compile, it must be changed to concept bool EqualityComparable).
If I remove requires Functor<F> from the mult_by_2 function declaration, then the code compiles and runs.
The problem is exactly what the error message says: Functor_Impl<F>::template fmap<int, int>(x) is not a valid expression. Functor_Impl<std::vector>::fmap has two parameters, not one.

Is libstdc++ wrong to reject assignment of volatile rvalue to std::ignore?

I noticed that libstdc++'s implementation of std::ignore takes a const T& argument, which can't bind to a volatile rvalue. Hence the following code fails to compile:
#include <tuple>
#include <utility>
struct C {};
using VC = C volatile;
int main() {
std::tuple<VC> t;
std::tie(std::ignore) = std::move(t);
}
(http://coliru.stacked-crooked.com/a/7bfc499c1748e59e)
Is this in violation of the standard, or is there a clause that renders this undefined behaviour?
I'm not a language lawyer, so I'm going to be answering this question as directly as possible.
ignore is found in the synopsis of tuple in tuple.general as such:
// [tuple.creation], tuple creation functions:
const unspecified ignore;
As you noticed, the libstdc++ implementation defines ignore like this:
// A class (and instance) which can be used in 'tie' when an element
// of a tuple is not required
struct _Swallow_assign
{
template<class _Tp>
const _Swallow_assign&
operator=(const _Tp&) const
{ return *this; }
};
Whereas the libc++ version defines it like this:
template <class _Up>
struct __ignore_t
{
template <class _Tp>
_LIBCPP_INLINE_VISIBILITY
const __ignore_t& operator=(_Tp&&) const {return *this;}
};
As such, it compiles in libc++. Now the definition of std::tie can be found in [tuple.creation] which says:
Returns: tuple<Types&...>(t...). When an argument in t is
ignore, assigning any value to the corresponding tuple element has
no effect.
This doesn't say anything about ignore itself, so I'm going to chalk this up to unspecified behavior. You can argue it's undefined behavior by omission, but that might be stretching it.
Comment:
// g++ 4.8.4
int main() {
volatile int vi;
std::ignore = vi;
// error: no match for ‘operator=’ (
// operand types are ‘const std::_Swallow_assign’
// and ‘std::remove_reference<volatile int&>::type {aka volatile int}’
// )
// std::ignore = std::move(vi);
// However this compiles:
volatile int&& vir = std::move(vi);
std::ignore = vir;
}

Custom class pointer as unordered map key

I'm trying to implement unordered_map with pointer to custom class as key and integer as value.
I thought pointer is just an address, so I wouldn't have to create comparison template for unordered_map, since map would compare between addresses. But I get compile error.
My code is as follow for simple testing. Can anyone help me to fix what have I done wrong?
#include <cstdlib>
#include <unordered_map>
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(int id){m_id = id;};
void PrintThis(){cout << " This is test " << endl;};
int m_id;
};
class Test{
public:
unordered_map<MyClass* mc, int test> mapTest;
};
int main(){
MyClass* mc1 = new MyClass(1);
MyClass* mc2 = new MyClass(2);
Test* tt1 = new Test();
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
tt1->mapTest.insert(make_pair<MyClass*, int>(mc2, 20));
auto search = tt1->find(mc1);
if(search != tt1->end()) {
search->first->PrintThis();
}else{
cout << "not Found " << endl;
}
}
Error message is as follow
./main.cpp:17:44: error: wrong number of template arguments (1, should be 5)
unordered_map<MyClass* mc, int test> mapTest;
^
In file included from /usr/include/c++/4.8/unordered_map:48:0,
from ./main.cpp:2:
/usr/include/c++/4.8/bits/unordered_map.h:97:11: error: provided for 'template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class std::unordered_map'
class unordered_map : __check_copy_constructible<_Alloc>
^
./main.cpp: In function 'int main()':
./main.cpp:26:18: error: request for member 'insert' in 'tt1->Test::mapTest', which is of non-class type 'int'
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
I think I can manage line 26 error, if line 17 gets fixed...
Thanks in advance!
I tried your code and found 3 problems:
Declaration of map: it should read std::unordered_map<MyClass*, int>
call of undefined functions (tt1->find/tt1->end, should read tt1->testMap.XXX)
Call of make_pair doesn't require template arguments. The compiler will infer them. This actually causes a problem, as the compiler tries to call make_pair(MyClass *&&, int &&). If I omit the template arguments, it works (make_pair(mc1, 10))
As for point 3:
make_pair is declared as follows in C++11 (C++14 just adds constexpr) (cppreference):
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
For template argument deduction, the follwing rule applies (cf. cppreference)
4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward)
(emphasis mine)
So the compiler will infer:
std::make_pair<MyClass *&, int>(MyClass *&, int &&);
where MyClass *& can bind to your actual argument.
If you directly specify the template types, the compiler will stick to
std::make_pair<MyClass *, int>(MyClass *&&, int &&).
As your argument is a lvalue, it cannot be converted to a rvalue-reference, and compilation fails
Your declaration of unordered_map<MyClass* mc, int test> mapTest; is invalid syntax. It should be unordered_map<MyClass*, int> mapTest.
Also remove the template parameters from your make_pair calls and change tt1->find to ttl->mapTest.find() and ttl->end() to ttl->mapTest.end().

Disable temporary binding of Eigen expression to const references

I am trying to write a function that accepts only lvalue Eigen expressions passed via const references. My first idea was to keep only the overload const Eigen::MatrixBase<Derived>& and delete the Eigen::MatrixBase<Derived>&& one. To my surprise, the deleted function was not part of the overload candidate set. So I tried the code below
#include <iostream>
#include <Eigen/Dense>
#define PRINT_MY_NAME std::cout << __PRETTY_FUNCTION__ << '\n'
template<typename Derived>
void f(const Eigen::MatrixBase<Derived>&) // (1)
{
PRINT_MY_NAME;
}
template<typename Derived>
void f(Eigen::MatrixBase<Derived>&&) // (2)
{
PRINT_MY_NAME;
}
int main()
{
Eigen::MatrixXd A;
f(A); // invokes (1)
f(A + A); // invokes also (1) !!!
}
which outputs (gcc5.2)
void f(const Eigen::MatrixBase&) [with Derived = Eigen::Matrix < double, -1, -1>]
void f(const Eigen::MatrixBase&) [with Derived = Eigen::CwiseBinaryOp < Eigen::internal::scalar_sum_op < double>, const Eigen::Matrix < double, -1, -1>, const Eigen::Matrix < double, -1, -1> >]
so clearly the rvalue overload is not considered. It is now clear for me that the second one is not a better match, since I pass a rvalue Eigen expression, which is convertible to Eigen::MatrixBase<>, but is not the exact same type. Now comes my question:
How can I disable or detect rvalue Eigen expressions passed as parameters for f? The issue is that an expression can have an arbitrary type (Eigen uses expression templates), like CwiseBinaryOp<...CwiseBinaryOp<...>> and so on. This is part of a larger problem in which I have a utility make-like function which takes a lvalue and binds it to a const reference expression in a class. If the expression is a rvalue, then all bets are off, since reference binding is not propagating via a constructor argument, so I want to forbid passing rvalue Eigen expressions.
I think I found what was going on: the result of the expression template A + A was a const, so there was a CV-mismatch. Adding const to the second overload does it:
template<typename Derived>
void f(const Eigen::MatrixBase<Derived>&&) // (2)
{
PRINT_MY_NAME;
}

T = char can't be deduced for std::basic_string<T> foo = "foo"?

Question:
In the code below, template argument type deduction seems to fail for the first sample, but not for the second sample. I don't understand why the first sample fails to deduce T = char. I would think that T can be deduced when converting from "foo" to std::bacis_string<T>, but even if that didn't work, I provide the second function argument which, I would think, would clearly constrain T to char. Why does it fail?
Does not work:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string bar = "bar";
print("foo", bar);
}
Error:
string.cpp:14:5: error: no matching function for call to 'print'
print("foo", bar);
^~~~~
string.cpp:6:6: note: candidate template ignored: could not match
'basic_string<type-parameter-0-0, char_traits<type-parameter-0-0>,
allocator<type-parameter-0-0> >' against 'char const[4]'
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
^
1 error generated.
Works:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string foo = "foo";
std::string bar = "bar";
print(foo, bar);
}
The problem is a conversion is required here. To deduce T, the compiler would have to inspect all possible instantiations of std::basic_string and see which of them can be constructed from a const char* (or actually const char (&)[4]). That's of course not possible, as there's infinitely many of them. The reason why it has to inspect all and cannot just scan the primary template definition for constructors taking const char* or const char(&)[4] is that for some T, std::basic_string<T> could be partially or completely specialised, and the members of those specialisations have no relationship to the members of the primary template.
Here's the short version of an answer.
The compiler has char const[] and is looking to convert that to std::basic_string<T>. How does it work out what T is? You know that you want to match T = char but the compiler does not know that.
It could look for a constructor basic_string<T>(char const *), for example. Even if that exists, it still does not say what T should be.
The compiler doesn't iterate over all possible typenames it knows about and attempt basic_string<T> for each one, and then see if there is a matching constructor.
Similar example:
template<typename T>
struct Foo
{
Foo(T t) {}
};
int main()
{
Foo(0); // error, can't deduce Foo<int>
}