Another problem with decltype - c++

//THIS IS JUST A FRAGMENT OF A static_numeric_limits.h for the purpose of this example
#include <limits.h>
template<class T>
struct static_numeric_limits;
template<>
struct static_numeric_limits<signed char>
{/*min was outside of range for enum*/
static const signed char min = SCHAR_MIN,
max = SCHAR_MAX;
};
/*This "surplus" template is here for the reason that char is threated differently from signed char */
template<>
struct static_numeric_limits<char>
{/*min was outside of range for enum*/
static const char min = SCHAR_MIN,
max = SCHAR_MAX;
};
template<>
struct static_numeric_limits<unsigned char>
{
static const unsigned char min = 0x0,
max = UCHAR_MAX;
};
///REAL PROBLEM STARTS FROM HERE
template<class IntType,IntType low_range = static_numeric_limits<IntType>::min>
struct Int
{
Int():value_(IntType())
{}
Int(const IntType& pattern)
{
value_ = (pattern);
}
constexpr inline IntType getValue()const
{
return value_;
}
private:
IntType value_;
};
template<class IntType,class IntType_1>
auto operator+
(Int<IntType>& lhs, Int<IntType_1>& rhs)
-> Int<decltype(lhs.getValue() + rhs.getValue())>//HERE IS THE PROBLEM
{
return lhs.getValue() + rhs.getValue();
}
Error (from VS2010)
error C2027: use of undefined type 'static_numeric_limits<T>'
Error (from gcc 4.6)
error: 'decltype ((lhs->getValue() + rhs->getValue()))' is not a valid type for a template constant parameter
Why doesn't this work as I thought it would?

The error here is what type decltype is deducing from your expression; unfortunately the error messages aren't clear about it, and it's actually a bit of a tricky problem.
Consider the type of the expression 0 + 0. It's an int, yes, but more importantly it's an rvalue (informally, it's a temporary). This means that decltype(0 + 0) is not int, but int&&. Now consider that your code isn't any different, in this regard: you still have an rvalue.
The problem is that template non-type parameters cannot be rvalue references, so you cannot have Int<int&&>, because of the second parameter's type . What you can do, though is this:
#include <type_traits>
// ...
template <class IntType, class IntType_1>
auto operator+(const Int<IntType>& lhs, // be const-correct!
const Int<IntType_1>& rhs)
-> Int<typename std::remove_reference<
decltype(lhs.getValue() + rhs.getValue())>::type>
{
return lhs.getValue() + rhs.getValue();
}
This takes the reference off int&&, giving you the bare int type. Hopefully gcc's error message makes a bit more sense: it's trying to tell you that you can't use int&& for your non-type parameter.
Another problem, though probably a non-issue, is that integer arithmetic undergoes what's called the usual arithmetic conversions. So the result of adding the values of two Int<char>'s is actually going to be an int, so your return type should be Int<int> (and is, with the fixed code).
The problem, then, is that you haven't defined static_numeric_limits<int>. But like I said, I suspect this is a non-issue and you do actually have it defined, just not displayed in your question.

Related

Convert a C++ integral type template parameter to a "longer" integral type

I have a templated function that accepts an integral type and performs a mathematical operation on two input parameters:
template<typename T, typename R>
R multiply( const T& lhs, const T& rhs ) {
R ans = static_cast<R>( lhs ) * static_cast<R>( rhs );
return ans;
}
(For simplicity, assume that types T and R are always unsigned integral types, or that I perform a suitable check to make sure.)
In this contrived example, you can see I am doing a simple multiplication (just for illustration). The idea is to pass a type R that is twice-as-many-bits-wide as type T; this will allow the full product to fit in the return value:
uint64_t Product64 = multiply<uint32_t,uint64_t>( 0xFFFFFFFF, 0xFFFFFFFF );
uint16_t Product16 = multiply<uint8_t,uint16_t>( 0xFF, 0xFF );
I am wondering if there is any way to dispense with the second template parameter, and automatically compute a type that is [at least] twice-as-many-bits-wide as type T at compile time.
Note: Just using the largest available integral type (uint64_t) for R at all times is not a solution for me. Ideally, I want a compile-time-computed type that is just large enough to hold twice-as-many-bits as type T (e.g. when T = uint8_t, R = uint16_t).
It seems to me this should be possible with limits or type_traits or something similar, but so far I haven't thought of a solution. C++11 or later version is fine. I would prefer to avoid boost, but if that is the only way, I would be interested to see a boost-based solution too.
You can make your own template sized types and use them to define your return type, like follows:
#include <cstdint>
template<int N>
struct sized_uint {};
template<> struct sized_uint<8 > { using type=std::uint8_t; };
template<> struct sized_uint<16> { using type=std::uint16_t; };
template<> struct sized_uint<32> { using type=std::uint32_t; };
template<> struct sized_uint<64> { using type=std::uint64_t; };
#include <climits>
template<typename T>
auto multiply(T lhs, T rhs)
{
using R=typename sized_uint<CHAR_BIT*sizeof(T)*2>::type;
return static_cast<R>(static_cast<R>(lhs) * static_cast<R>(rhs));
}
See it live.

constexpr operator overloading issues with using arguments

I am making a simple class inheriting from std::array. The point is that it should throw a compile time error if the subscript operator is used for an out of bounds index. However, I keep getting an error message. This is the code simplified.
#include <array>
using namespace std;
template<typename type, size_t size>
struct container : array<type,size>
{
constexpr inline type& operator[](int index) const
{
static_assert(index<size,"");
return ((static_cast<const array<type,size> >(*this))[index]);
}
template<class... bracelist>
constexpr container(bracelist&&... B)
:array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[2];
}
The error it gives me is:
main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression
However, I used "index" in the return statement, and commenting out the static_assert makes it work fine. If index was not a constant expression, wouldn't I not be able to use it in the subscript operator for std::array after the static_cast? I am new to using the constexpr functionality, so any help would be appreciated. Thank you.
Note: I am aware std::array's constexpr subscript operator already does this, I just want to know how to do this for future uses. Thanks.
There are 2 really useful features of constexpr functions, the interplay of which is not always fully appreciated.
In constexpr context they only evaluate code paths that are taken for the constexpr arguments.
In non-constexpr context they behave exactly like regular functions.
Which means that we can use exceptions to great effect.
Since while in constexpr context, if the exception path is taken, this is a compiler error (throw is not allowed in constexpr context). You get to see the "exception" in your compiler's error output.
example:
#include <array>
#include <stdexcept>
template<typename type, std::size_t size>
struct container : std::array<type,size>
{
constexpr auto operator[](std::size_t index) const
-> type const&
{
if (index < size)
return static_cast<const std::array<type,size>>(*this)[index];
else
throw std::out_of_range("index out of range" + std::to_string(index));
}
template<class... bracelist>
constexpr container(bracelist&&... B)
: std::array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[4];
}
Example output:
main.cpp: In function 'int main()':
main.cpp:28:37: in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
throw std::out_of_range("index out of range" + std::to_string(index));
This approach is actually more versatile than static_assert, since it works at both compile and runtime.
The thing you have to keep in mind is that constexpr functions can be called at runtime with non constexpr arguments. constexpr means for a function that the function is usable in a compile-time evaluated expression (e.g. another constexpr or a template argument) but not exclusively. A constexpr function can still be called in the classical way, i.e. at run-time with run-time variables. Which means that the parameters of a constexpr function cannot be and are not compile-time constants.
It doesn't apply to your case but in general if you know a parameter will always be called with a compile time constant than you can make it a template parameter.
constexpr void foo(int a)
{
static_assert(a != 0); // illegal code because the parameter
// cannot be a compile time constant
}
void test()
{
int x;
std::cin >> x;
foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
static_assert(N != 0); // legal
}
void test()
{
int x;
std::cin >> x;
foo<x>(); // illegal, x is not a compile time constant
foo<24>(); // legal
constexpr int c = 11;
foo<c>();; // legal
}
This is the reason for std::get<N>(array) — it is the only way to assuredly pass a "compile-time value" in a manner that'll satisfy the rules of the language. Your attempt to create a compile-time op[] cannot work. You could of course make your own templated accessor like std::get, but one might ask why not just use std::array as it is already.

Is it possible to write a single named access function for both, constant reference and writeable reference of elements of a tuple?

I've got some hierarchy of std::tuple.
I would like to write some access functions for all these tuples so that the resulting code is more readable.
So instead of writing:
std::get<2>(std::get<1>(std::get<0>(s)))
I would rather prefer to write
getNetName(getFirstNet(getFirstOutput(s)))
Now the point is to avoid writing these access functions twice -- for constant parameters and for writeable parameters.
Can this be done?
And of course -- I want these access functions to be type-safe. There might be multiple tuples types to which std::get<0>() can be applied -- but I would rather prefer that getNetName() creates a compiler error if it is not applied to a net.
std::get already does exactly what you want, so your goal is to simply write an alias for an existing function. The trick is to perfect forward your argument and return value to ensure nothing is lost. For example :
#include <tuple>
template<class T>
decltype(auto) getFirstElem(T&& p_tuple)
{
return std::get<0>(std::forward<T>(p_tuple));
}
int main()
{
std::tuple<int, int> mutable_x = { 42, 24 };
const std::tuple<int, int> const_x = { 10, 20 };
// mutable_ref is a `int&`
auto&& mutable_ref = getFirstElem(mutable_x);
// const_ref is a `const int&`
auto&& const_ref = getFirstElem(const_x);
}
decltype(auto) ensure that the return value is perfect forwarded. This preserves the reference qualifier and the constness of the return type. Using auto would cause the return value to decay to the underlying value type (in this case int). auto&& is similarly used to capture the result without discarding reference qualifier or constness.
Edit : It seems there was a type safety component to the question I missed. This is easily fixed by introducing a static_assert and std::is_same to compare the argument type with the expected type. It's important to remove reference qualifiers and cv modifiers to ensure the comparison is correct.
template<class T>
decltype(auto) getFirstElem(T&& p_tuple)
{
using t_expected = std::tuple<int, int>;
// Verify that the tuple matches the expectations
using t_tuple_clean = std::remove_cv_t<std::remove_reference_t<T>>;
static_assert(std::is_same<t_expected, t_tuple_clean>::value, "Unexpected tuple type");
return std::get<0>(std::forward<T>(p_tuple));
}
Unfortunately, the error message will usually be pretty long. Unfortunately I don't see a way to write this where the compiler's built-in argument matching could be used (which would generate clearer error messages). Perfect forwarding requires that the argument be a template type. Otherwise, you would need two overloads (one for const and one for non-const arguments) which would violate the single-function requirement of the question.
If you find it annoying to write out the check for every function, you can write a helper which can be used to more easily write new access functions.
#include <tuple>
#include <type_traits>
template<size_t index, class t_expected, class t_tuple>
decltype(auto) getHelper(t_tuple&& p_tuple)
{
// Verify that the tuple matches the expectations
using t_tuple_clean = std::remove_cv_t<std::remove_reference_t<t_tuple>>;
static_assert(std::is_same<t_expected, t_tuple_clean>::value, "Unexpected tuple type");
// Forward to std::get
return std::get<index>(std::forward<t_tuple>(p_tuple));
}
template<class T>
decltype(auto) getFirstElem(T&& p_tuple)
{
return getHelper<0, std::tuple<int, int>>(std::forward<T>(p_tuple));
}
The access function will now fail with a compiler error if the wrong type of tuple is provided :
int main()
{
// Compiler error 'Unexpected tuple type'
std::tuple<double, int> bad_tuple{};
auto&& bad_ref = getFirstElem(bad_tuple);
}
sorry this is C++11 (complain to my boss)!
template<typename T, std::size_t I>
struct get
{ auto operator()(T &_r) const -> decltype(std::get<I>(_r))
{ return std::get<I>(_r);
}
auto operator()(const T &_r) const -> decltype(std::get<I>(_r))
{ return std::get<I>(_r);
}
};
and application:
typedef std::pair<
std::size_t, // starting value
std::size_t // size or ending value?
> dimension;
typedef get<dimension, 0> getDimensionStart;
typedef get<dimension, 1> getDimensionSize;
typedef std::pair<
std::string,
boost::optional<dimension> // if scalar, then this is empty
> port;
typedef get<port, 0> getPortName;
typedef get<port, 1> getPortDimension;
using this code would look like this:
const port s("name", dimension(0, 10));
std::cout << getDimensionStart()(*getPortDimension()(s)) << std::endl;

What the type is auto & x = const int *?

In a main function, I created a variable of const int pointer, assign it to a variable declared by auto&. Then using the decltype(x) to check the type. I expected the type is const int*. But is_same returns false.
int main()
{
int a = 10;
const int * cp_val= &a;
auto& x = cp_val;
bool is_const_int_ptr = std::is_same<decltype(x), const int *>::value; // returns 0
// *x = 100; // error: assignment of read-only location '* x'
}
But if I add the following helper function:
#include <boost/type_index.hpp>
template<typename T>
void print_type(T)
{cout << "type T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< '\n';}
In the main, I invoke the function
print_type(x); // It returns int const*
Am I missing something in std::is_same?
Note that for auto& x, you're declaring x as reference explicitly; then its type should be const int *&, i.e. a reference to pointer to const int.
Here's a better idea (from Effective Modern C++ (Scott Meyers)) to get the accurate type at compile time from the compiling error message.
template <typename>
struct TD;
then use it as
TD<decltype(x)> td;
you'll get error message like
source_file.cpp:15:21: error: implicit instantiation of undefined template 'TD<const int *&>'
TD<decltype(x)> td;
^
LIVE
Your helper function takes parameter by value; the reference-ness of the argument will be ignored in type deduction, that's why you got const int*.
Template argument deduction and auto are intimately related: The declaration auto x = e; gives x the same type as f(e) would give to T in an invented function template <typename T> f(T);, and similarly for auto& and f(T&), const auto* and f(const T*), etc.
Therefore, to get the correct answer from Boost, you need to declare:
template <typename T> void print_type(T&);
// ^^^^
The type of x is of course const int*&.

Why member variables of a const object are not const

Just asked a similar question which boils down to this one.
#include <iostream>
using namespace std;
struct A {
A() : a{1} {};
int a;
};
template <typename Which>
struct WhichType;
int main() {
const A a;
const A& a_ref = a;
const A* a_ptr = &a;
WhichType<decltype(a.a)> which_obj; // template evaluates to int
WhichType<decltype(a_ref.a)> which_ref; // template evaluates to int
WhichType<decltype(a_ptr->a)> which_ptr; // template evaluates to int
return 0;
}
Why do the templates do not become const int instead of int?
decltype gives you the "declared type" of the operand when it isn't enclosed in an extra set of parentheses.
To get the actual type of the expression, that is, const int, you would have to write decltype((a.a)) and so on.
decltype always returns a reference type for lvalue expressions other than names.
When passed the name of an identifier (or member), it returns the type of declaration.
When passed a different expression, it returns something closer to what you want, but reference-qualified.
WhichType<std::remove_reference_t<decltype((a_ptr->a))>> which_ptr; // template evaluates to const int!
live example
or if you want the l/r valueness:
WhichType<decltype((a_ptr->a))> which_ptr2; // template evaluates to const int&
WhichType<decltype(((const A){}.a))> which_ptr3; // template evaluates to const int
you can append && to make it a "real" rvalue reference here.
WhichType<decltype(((A){}.a))&&> which_ptr4; // template evaluates to int&&!
live example.