Related
I want to have a class representing a unit with some kind of dimension. This should express something like 1.5m^2. A scalar multiplication with some type shall be allowed and a dimensionless unit should behave exactly like the underlying type. Here is my solution:
#include <type_traits>
template<typename T, int Dim>
class Unit {
public:
explicit Unit(T t): _value(t) {}
template<int D = Dim, typename std::enable_if_t<D==0, int> = 0>
operator T() { static_assert(Dim==0, ""); return _value; } //static_assert not necessary, but gives error if template is removed
T _value;
};
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
This works perfectly fine in clang (https://godbolt.org/z/8Pev7W6Y1). However, due to a GCC bug with templated conversion operators (Conversion operator: gcc vs clang), this does not work in GCC.
It is not possible to remove the SFINAE construction because it (correctly) runs into the static_assert.
Do you have an idea for equivalent code that also works in GCC? The code should work in C++17 with both compilers.
You can use specialization instead of SFINAE. To avoid too much duplication you can move the common parts (anything that does not depend on Dim) to a base class:
#include <type_traits>
template <typename T>
class base_unit {
public:
explicit base_unit(T t): _value(t) {}
T _value;
};
template<typename T, int Dim>
class Unit : public base_unit<T> {
public:
explicit Unit(T t): base_unit<T>(t) {}
};
template <typename T>
class Unit<T,0> : public base_unit<T> {
public:
explicit Unit(T t) : base_unit<T>(t) {}
operator T() { return base_unit<T>::_value; }
};
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
Live Demo
Note that this is a little old-fashioned and does not consider more modern C++20 approaches (for example the operator T() requires (Dim == 0) mentioned in a comment).
Do you have an idea for equivalent code that also works in GCC? The code should work in C++17 with both compilers.
Since you don't want the code on the call side to be changed and the problem is with y+i, you could overload operator+ as shown below:
#include <type_traits>
#include <iostream>
template<typename T, int Dim>
class Unit {
public:
explicit Unit(T t): _value(t) {}
template<int D = Dim, typename std::enable_if_t<D==0, int> = 0>
operator T() { static_assert(Dim==0, ""); return _value; } //static_assert not necessary, but gives error if template is removed
T _value;
//overload operator+
template<typename U,int D, typename V> friend U operator+( Unit<U,D>& u, V& v);
};
//define overloaded operator+
template<typename U, int D, typename V> U operator+( Unit<U,D>& u, V&v)
{
std::cout<<u.operator U() + v;//just for checking the value
return u.operator U() + v;
}
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
The output the above program can be seen here.
Here's what I want to do:
#include <vector>
template <class ContainerType, typename ComparatorType>
void f(
ContainerType c1,
ComparatorType comp =
[](const typename ContainerType::value_type& l, const typename ContainerType::value_type& r) {return l < r;})
{
}
int main()
{
std::vector<int> a{1, 2};
f(a);
return 0;
}
But it doesn't work: could not deduce template argument for 'ComparatorType'.
Using a proxy function instead of an actual default argument value works, but seems overly verbose, isn't there a better way? Not to mention it's not the same since now I can't just substitute the default comparator with my own without changing the function name in the client code.
#include <vector>
template <class ContainerType, typename ComparatorType>
void f(
ContainerType c1,
ComparatorType comp)
{
}
template <class ContainerType>
void f2(ContainerType c)
{
f(c, [](const typename ContainerType::value_type& l, const typename ContainerType::value_type& r) {return l < r;});
}
int main()
{
std::vector<int> a{1, 2};
f2(a);
return 0;
}
without changing the function name in the client code.
You can overload function templates just fine. There is no need to use a different name.
template <class ContainerType, typename ComparatorType>
void f(
ContainerType c1,
ComparatorType comp)
{
}
template <class ContainerType>
void f(ContainerType c)
{
f(c, [](const typename ContainerType::value_type& l, const typename ContainerType::value_type& r) {return l < r;});
}
You can't make a default function argument contribute to template argument deduction. It's not allowed because it raises some difficult to resolve questions in the deduction process.
Template deduction is performed before default arguments are considered. Also, lambdas are not allowed to appear in unevaluated operands.
You can first assign the default function to a variable. Then you can spell out its type. For example:
auto default_functor = [](int x){ return x > 0; };
template <typename T, typename F = decltype(default_functor)>
auto function(T x, F f = default_functor)
{
return f(x);
}
Now you can use the function as usual:
bool even(int x)
{
return x % 2 == 0;
}
struct Odd {
bool operator()(int x) const
{
return x % 2 == 1;
}
};
void g()
{
function(1); // use default functor
function(1, even); // use a free function
function(1, Odd{}); // use a function object
function(1, [](int x){ return x < 0; }); // use another lambda
}
You don't need to explicitly specify the type.
Note: According to #StoryTeller, this can lead to ODR violation if you use it in a header. In that case, you can use a named functor type:
struct Positive {
constexpr bool operator(int x) const
{
return x > 0;
}
};
inline constexpr Positive default_functor{};
template <typename T, typename F = decltype(default_functor)>
auto function(T x, F f = default_functor)
{
return f(x);
}
I have a c++11 type alias:
using coord = std::array<double, 3>;
Can I define the operator + for coord? How? I'd want to be able to do:
coord a, b, c;
a = {1.1, 2.0, 0};
b = {0, -1, 3.5};
c = a + b; // c = {1.1, 1.0, 3.5}
I wouln't recommend defining new operators for STL type, or any types that aren't in your control.
I would suggest instead create your own types:
struct coord {
// reimplement operator[], fill, size, etc.
friend constexpr auto operator+ (coord const& lhs, coord const& rhs) noexcept -> coord {
// ...
}
private:
std::array<double, 3> _coord;
};
Or you could also shortcut your way around using operators and member function already defind in std::array:
struct coord : private std::array<double, 3> {
constexpr coord(double a, double b, double c) noexcept :
array{a, b, c} {}
using array::operator[];
using array::begin;
using array::end;
using array::fill;
// other operators...
friend constexpr auto operator+ (coord const& lhs, coord const& rhs) noexcept -> coord {
// ...
}
};
Note: If you want to support structured binding and offer tuple like access to your coord class, then you must add overloads for get and specialize std::tuple_size and std::tuple_element:
namespace std {
template<size_t n>
tuple_element<n, ::coord> {
using type = double;
};
tuple_size<::coord> : integral_constant<size_t, 3> {};
}
template<std::size_t n>
constexpr auto get(coord const& c) noexcept -> double const& {
return c[n]
}
template<std::size_t n>
constexpr auto get(coord& c) noexcept -> double& {
return c[n];
}
template<std::size_t n>
constexpr auto get(coord&& c) noexcept -> double&& {
return std::move(c[n]);
}
template<std::size_t n>
constexpr auto get(coord const&& c) noexcept -> double const&& {
return std::move(c[n]);
}
This code will allow that kind of code when you switch C++17 on:
auto my_coord = coord{1.0, 2.0, 3.0};
auto [x, y, z] = my_coord;
I want to add why you should not add an operator+ for std::array. Or, more generally, why you should only add operators (or rather, free functions) to the namespace of the type they operate on (which is forbidden in the case of std).
For one, you can't put the operator in namespace A and then properly use it in namespace B (without using namespace A;):
namespace A
{
using coord = std::array<double, 3>; // Putting this in the global namespace doesn't help.
coord operator+(const coord& right, const coord& left)
{
return coord { right[0] + left[0], right[1] + left[1], right[2] + left[2] };
}
}
auto foo()
{
A::coord x, y;
return x + y; // doesn't see the operator
}
https://godbolt.org/z/XUR_NX
The only place left to put the operator is the global namespace. If that doesn't make you uncomfortable, here is a reason why it should: The name lookup rules will break your neck soon enough.
What if we want to write a template that uses your operator+? It should work fine, right?
template<class ... T>
auto add(T ... t)
{
return (t + ...);
}
https://godbolt.org/z/Ox8_r5
Yep, it works! Well, until someone adds any operator+ closer to the template:
namespace A
{
struct X{};
void operator+(X, X);
template<class ... T>
auto add(T ... t)
{
return (t + ...); // won't find the correct operator+ for T=coord
}
}
https://godbolt.org/z/ctcfxr
Name lookup stops at that operator+. Since your correct one (for std::array) is not in namespace std (where std::array is), it cannot be found by ADL (argument dependent lookup). At that point you are out of options.
I'm wondering if it's possible to convert simple loop that is invoked through a parameter pack into a constexpr with simpler code. This example code demonstrates what I'm trying to do
struct Student {
AgeCategory age;
Income income;
bool is_student;
CreditRating credit_rating;
bool buys_computer;
};
auto find_probability(const double x, const double mean, const double stdev) -> double;
typedef std::tuple<double, double> MeanStdDev;
typedef std::vector<MeanStdDev> MeanStdDevVec;
// This code seems verbose to me. Is there a simpler way to express this
// loop which iterates over a vector and parameter pack, possibly
// using constexpr. C++14/17 idioms are fine.
template<typename Attr>
auto get_probability(const MeanStdDevVec& v, const size_t n, const Student& s, Attr attr) -> double {
double mean, stdev;
std::tie(mean, stdev) = v[n];
return find_probability(static_cast<double>(std::invoke(attr, s)), mean, stdev);
}
template<typename First, typename... Attr>
auto get_probability(const MeanStdDevVec& v, const size_t n, const Student& s, First f, Attr... attr) -> double {
double mean, stdev;
std::tie(mean, stdev) = v[n];
return find_probability(static_cast<double>(std::invoke(f,s)), mean, stdev) * get_probability(v, n + 1, s, attr...);
}
template<typename ...Attr>
auto calculate_class_probability(const std::map<bool, MeanStdDevVec>& summaries, const Student& s, Attr... attributes) {
for (const auto& i : summaries) {
get_probability(i.second, 0L, s, attributes...);
}
}
called from
Student s;
calculate_class_probability(class_summaries, s , &Student::age, &Student::income, &Student::credit_rating, &Student::is_student);
It doesn't necessarily make the code shorter as a whole, but it does separate out a generic part that you can reuse easily, and IMHO makes the code clearer. The key in this particular case is a function that maps packs into arrays of a certain type:
template <class T, class F, class ... Args>
std::array<T, sizeof...(Args)> pack_to_array(F f, Args&& ... args) {
return {(f(std::forward<Args>(args)))...};
}
In your case, this isn't quite enough, as you want to zip it with a vector. So a useful modification of this, would be to make the integer index of the pack element available and pass it to the function:
template <class T, class F, class ... Args>
std::array<T, sizeof...(Args)> index_pack_to_array(F f, Args&& ... args) {
std::size_t i = 0;
return {(f(i++, std::forward<Args>(args)))...};
}
Now, you can use this function like so:
template<typename... Attr>
double get_probability(const MeanStdDevVec& v, const Student& s, Attr... attr) {
assert(v.size() == sizeof...(Attr));
auto probs = index_pack_to_array<double>(
[&] (std::size_t i, auto&& a) -> double {
return // ... (get probability from v[i], s, and a)
},
std::forward<Attr>(attr)...);
return std::accumulate(probs.begin(), probs.end(), 1.0,
[] (double p1, double p2) { return p1 * p2; });
}
Not sure to understand what do you want, but I suppose you can throw away your getProbability() and rewrite calculate_class_probability() as follows
template <typename ... Attr>
auto calculate_class_probability
(std::map<bool, MeanStdDevVec> const & summaries,
Student const & s, Attr ... attributes)
{
using unusedT = int[];
for ( const auto & i : summaries )
{
double mean, stdev;
double prob {1.0};
std::size_t n {0};
(void)unusedT { (std::tie(mean, stdev) = i.second[n++],
prob *= find_probability(static_cast<double>(std::invoke(attributes,s)), mean, stdev),
0)... };
// in prob the calculate value
}
}
But: no: I don't think it's possible to write this as constexpr; the operator[] for std::vector<> isn't constexpr
Say I want a C++ function to perform arithmetic on two inputs, treating them as a given type:
pseudo:
function(var X,var Y,function OP)
{
if(something)
return OP<int>(X,Y);
else if(something else)
return OP<double>(X,Y);
else
return OP<string>(X,Y);
}
functions that fit OP might be like:
template <class T> add(var X,var Y)
{
return (T)X + (T)Y; //X, Y are of a type with overloaded operators
}
So, the question is what would the signature for function look like? If the operator functions are non-templated I can do it, but I get confused with this extra complexity.
Template functions cannot be passed as template arguments. You have to manually deduce template arguments for this function before you pass it to another template function. For example, you have function
T sum(T a, T b)
{
return a + b;
}
You want to pass it to callFunc:
template<typename F, typename T>
T callFunc(T a, T b, F f)
{
return f(a, b);
}
You can't simply write
int a = callFunc(1, 2, sum);
You have to write
int a = callFunc(1, 2, sum<int>);
To be able to pass sum without writing int, you have to write a functor - struct or class with operator() that will call your template function. Then you can pass this functor as template argument. Here is an example.
template<class T>
T sum(T a, T b)
{
return a + b;
}
template<class T>
struct Summator
{
T operator()(T a, T b)
{
return sum<T>(a, b);
}
};
template<template<typename> class TFunctor, class T>
T doSomething(T a, T b)
{
return TFunctor<T>()(a, b);
//Equivalent to this:
//TFunctor<T> functor;
//return functor(a, b);
}
int main()
{
int n1 = 1;
int n2 = 2;
int n3 = doSomething<Summator>(n1, n2); //n3 == 3
return 0;
}
Are you looking for this?
template<class T> T add(T X, T Y)
{
return X + Y;
}
Or are you looking for something that calls something like add?
template<class T, class F>
T Apply(T x, T y, F f)
{
return f( x, y );
}
Called via:
int x = Apply( 2, 4, add<int> );
I'm a bit confused … why the type differentiation in your pseudo-code?
C++ templates allow full type deduction on templates:
template <typename T, typename F>
T function(T x, T y, F op) {
return op(x, y);
}
Here, F fits anything (especially functions) that may be called with the () function call syntax and accepting exactly two arguments of type T (or implicitly convertible to it).
I use lambdas for this.
auto add = [](const auto& lhs, const auto& rhs) {
static_assert(std::is_arithmetic<typename std::decay<decltype(lhs)>::type>::value,
"Needs to be arithmetic.");
static_assert(std::is_arithmetic<typename std::decay<decltype(rhs)>::type>::value,
"Needs to be arithmetic.");
return lhs + rhs;
};
template<typename LHS, typename RHS, typename FUNC
, typename OUT = typename std::result_of<FUNC(LHS, RHS)>::type>
constexpr OUT do_arithmetic(LHS lhs, RHS rhs, FUNC func) {
return func(lhs, rhs);
}
constexpr auto t = do_arithmetic(40, 2, add);
static_assert(t == 42, "Wrong answer!");
static_assert(std::is_same<std::decay<decltype(t)>::type, int>::value,
"Should be int.");
template <class OP> void function(OP op)
{
// call with int
op(1, 2);
// or with double
op(1.2, 2.3);
// call with explicit template argument
op.template operator()<int>(1, 2);
op.template operator()<string>("one", "two");
}
struct Add
{
template <class T> T operator ()(T a, T b)
{
return a + b;
}
};
function(Add());
// or call with C++14 lambda
function([](auto a, auto b) { return a + b; });
I think you're looking for the Strategy Pattern.
I'm not sure what this var thing in your question means. It's certainly not a valid C++ keyword, so I assume it's a type akin to boost:any. Also, the function is missing a result type. I added another var, whatever that might be. The your solution could look like this:
template< template<typename> class Func >
var function(var X, var Y, Func OP)
{
if(something)
return OP<int>(X,Y);
else if(something else)
return OP<double>(X,Y);
else
return OP<string>(X,Y);
}
The funny template argument is a template itself, hence its name "template template argument". You pass in the name of a template, not an instance. That is, you pass std::plus, not std::plus<int>:
return function( a, b, std::plus );