C++11, Is it possible to force an instance to be extern but also a constant expression of a non-type template parameter? - c++11

Using C++11, g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-18).
Lets pretend I have a templated function (pardon my terminology if it isn't quite right).
I want to perform a "general" algorithm based on what was supposed to be compile-time instances of "field". Where the only things that really changed are these constants which I moved into trait classes (only added one here but imagine there are more). Originally I was declaring it as
constexpr field FIELD1{1};
However in C++11, non-type template params need to have external linkage (unlike C++14 which can have internal and external linkage?). So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr and it seems that losing that constexpr constructor this field is no longer a valid constant expression to qualify as a non-type template param.
Any suggestions if there is some way I can get around this? Open to a new method of doing things. Below is a simplified (incomplete, and non-compiling version to get the gist of the organization).
So the error I am seeing is along the lines of
error: the value of ‘FIELD1’ is not usable in a constant expression
note: ‘FIELD1’ was not declared ‘constexpr’
extern const field FIELD1;
Not quite sure what could be a best alternative.
I can get rid of the second error by removing the constexpr from the constructor. But then I don't know how to approach the constant expression issue.
field.H
struct field
{
int thingone;
constexpr field(int i):thingone(i){}
};
extern const field FIELD1;
field.C
#include "field.H"
const field FIELD1{0};
field_traits.H
#include "field.H"
template< const field& T >
class fieldTraits;
template< >
class fieldTraits<FIELD1>
{
public:
// Let's say I have common field names
// with different constants that I want to plug
// into the "function_name" algorithm
static constexpr size_t field_val = 1;
};
function.H
#include "field.H"
template< const field& T, typename TT = fieldTraits<T> >
void function_name()
{
// Let's pretend I'm doing something useful with that data
std::cout << T.thingone << std::endl;
std::cout << TT::field_val << std::endl;
}

So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr [...]
Per my comment, you can. It wouldn't work for you, but it's a step that helps in coming up with something that would work:
extern constexpr int i = 10;
This is perfectly valid, gives i external linkage, and makes i usable in constant expressions.
But it doesn't allow multiple definitions, so it can't work in a header file which is included in multiple translation units.
Ordinarily, the way around that is with inline:
extern inline constexpr int i = 10;
But variables cannot be declared inline in C++11.
Except... when they don't need to be declared inline because the effect has already been achieved implicitly:
struct S {
static constexpr int i = 10;
};
Now, S::i has external linkage and is usable in constant expressions!
You may not even need to define your own class for this, depending on the constant's type: consider std::integral_constant. You can write
using i = std::integral_constant<int, 10>;
and now i::value will do exactly what you want.

Related

Remove class member type part from decltype

I ran into I case I had not seen before, while using decltype on a member of a templated class. I wanted to make a nicer make_unique so that changing type on the member does not cause fixing the make_unique calls. I wanted to avoid this using decltype(member)::element_type as the type for make_unique but got an error. Here is a simple snippet that shows the error (and I understand why it is shown):
#include <memory>
template<typename T>
struct foo
{
foo()
{
// g++ gives:
// dependent-name 'decltype (((foo<T>*)this)->foo<T>::p_)::element_type' is parsed as a non-type, but instantiation yields a type
// say 'typename decltype (((foo<T>*)this)->foo<T>::p_)::element_type' if a type is meant
//
// How can I atleast remove the class name from the type?
p_ = std::make_unique<decltype(p_)::element_type>();
// g++ gives:
// dependent-name 'decltype (p)::element_type' is parsed as a non-type, but instantiation yields a type
// say 'typename decltype (p)::element_type' if a type is meant
//
// makes sense since p here is dependent on T
std::unique_ptr<T> p = std::make_unique<decltype(p)::element_type>();
// This one is fine, makes sense, since the type is known
std::unique_ptr<int> p2 = std::make_unique<decltype(p2)::element_type>();
}
std::unique_ptr<T> p_;
};
int main()
{
foo<int> f;
return 0;
}
My question is, is there a nice/pretty way to remove the 'is a member of' ((foo<T>*)this)->foo<T>::p_))part from the decltype value, so that at least I could use the same fix and simply provide typename on the member variable p_ ? The long fix suggested by g++ seems kind of ugly.
5 minutes after posting I had an idea that I could do
p_ = std::make_unique<decltype(std::remove_reference(*p_)::type)>();
but that seems to give a parse error.
You can simply place a typename before decltype().
I mean
p_ = std::make_unique<typename decltype(p_)::element_type>();

std::vector of type deduced from initializers before C++17 ... any workaround for C++11?

I learned that from C++17, with the deduction guides, template arguments of std::vector can be deduced e.g. from the initialization:
std::vector vec = { function_that_calculate_and_return_a_specifically_templated_type() }
However I do not have the luxury of C++17 in the machine where I want to compile and run the code now.
Is there any possible workaround for C++11? If more solutions exist, the best would be the one that keep the readability of the code.
At the moment the only idea that I have is to track the various cases along the code (luckily they should not be too many) and make some explicit typedef/using.
Any suggestion is very welcome
The usual way to use type deduction for class template when CTAD is not available is providing a make_* function template, e.g. for your case (trailing return type is necessary for C++11):
#include <vector>
#include <type_traits>
#include <tuple>
template <class ...Args>
auto make_vec(Args&&... args) ->
std::vector<typename std::decay<typename std::tuple_element<0, std::tuple<Args...>>::type>::type>
{
using First = typename std::decay<typename std::tuple_element<0, std::tuple<Args...>>::type>::type;
return std::vector<First>{std::forward<Args>(args)...};
}
You can invoke the above with
const auto v = make_vec(1, 2, 3);
which gets at least kind of close to CTAD in the sense that you don't have to explicitly specify the vector instantiation.
While the answer by lubgr is a correct way, the following template is simpler and seems to work as well:
#include <vector>
#include <string>
template <typename T>
std::vector<T> make_vec(const std::initializer_list<T> &list)
{
return std::vector<T>(list);
}
int main()
{
auto v = make_vec({1,2,3});
auto v2 = make_vec({std::string("s")});
std::string s("t");
auto v3 = make_vec({s});
return v.size() + v2.size() + v3.size();
}
One advantage of using the initializer_list template directly are more clear error messages if you pass mixed types like in make_vec({1,2,"x"});, because the construction of the invalid initializer list now happens in non-templated code.

Why isn't typename interchangeable with decltype() for this usage?

For a given usage
template<typename T1>
class node{
public:
using sp2node = shared_ptr<node<T1>>;
using r_sp2node = shared_ptr<node<T1>>&;
public:
r_sp2Node getN();
private:
sp2node N;
};
(1)
template<typename T1> decltype(node<T1>::r_sp2node) node<T1>::getN(){
return N;
}
(2)
template<typename T1> typename node<T1>::r_sp2node node<T1>::getN(){
return N;
}
(1) generates the compiler error:
error: missing 'typename' prior to dependent type name
'node<T1>::r_sp2node'
whereas (2) compiles
Can someonene explain what is the difference between the above two?
There are two things wrong with the first example.
In decltype(node<T1>::r_sp2node), reading inside out, the compiler first needs to know what node<T1>::r_sp2node. Is it a type, or something else? This is why the typename disambiguator exists, and that is what the error message is all about.
The second problem is that decltype expects some expression, not a type. So even if you used typename, it still wouldn't compile. (As a simple example, decltype(int) won't even compile.)
To answer the question specifically, the difference between the two is that the first is not valid C++ and the second is the way to go.

Passing an unspecialised template to another template

Given the following code here in IDEOne:
#include <iostream>
#include <vector>
#include <list>
template<typename T>
class MyVectorCollection
{
using collection = std::vector<T>;
};
template<typename C, typename T>
class MyGenericCollection
{
using collection = C;
};
template<typename C, typename T>
class MyMoreGenericCollection
{
using collection = C<T>;
};
int main() {
// your code goes here
MyVectorCollection<int> a;
MyGenericCollection<std::list<int>, int> b;
MyMoreGenericCollection<std::list, int> c; // How to do this?
return 0;
}
I get the error:
prog.cpp:20:24: error: ‘C’ is not a template
using collection = C<T>;
^
prog.cpp: In function ‘int main()’:
prog.cpp:27:43: error: type/value mismatch at argument 1 in template parameter list for ‘template<class C, class T> class MyMoreGenericCollection’
MyMoreGenericCollection<std::list, int> c;
^
prog.cpp:27:43: note: expected a type, got ‘list’
How can I write my code such that I can use a C<T> at compile time without having an explicit list of potential specialisations, and avoiding macros, if possible? I realise std::list isn't a typename, but I don't know how to progress, and I have been unable to find a similar question here.
(Note that this is just an MCVE, my actual usage is much more involved.)
Just to tidy up, here is the solution. The search term I was looking for is template template, and whilst I did find a possible duplicate, I think this question and answer is much simpler to follow.
So, thanks to the hint from #Some programmer dude, I looked up template template and updated the code to this, which does compile:
template<template<typename, typename> class C, typename T>
class MyMoreGenericCollection
{
using collection = C<T, std::allocator<T>>;
};
We declare the first template parameter as a template itself, and remembering that the standard library constructors take two parameters, we need to make the inner template take two parameters. There is (as far as I am aware) no way to automatically use the default second parameter, so for the sake of this example I explicitly state the default.
I could of course add a third parameter to the master template that could be used to specify the allocator, which itself would also be a template, but I leave that as an exercise for th reader.

boost::variant and operator<< overloading

I wanted to test a simple thing like the following:
#include <iostream>
#include <boost/variant.hpp>
template<typename T1,typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1,T2>& dt){
os << dt.first << dt.second;
return os;
}
int main(){
boost::variant<int, std::pair<int,int>, bool> v;
v = std::pair<int,int>(3,3);
std::cout << v << std::endl;
}
This should actually work, because for normal types, like int, double and so on, it compiles.
boost::variant has a printer vistor which it uses internally to output the content to the stream.
Actually this fails to compile, but I do not really know the problem:
The codes fails here: in variant_io.hpp
template <typename OStream>
class printer
: public boost::static_visitor<>
{
private: // representation
OStream& out_;
public: // structors
explicit printer(OStream& out)
: out_( out )
{
}
public: // visitor interface
template <typename T>
void operator()(const T& operand) const
{
out_ << operand; // HEEEEEEERRRRREE!!!!!!!!!!!!
}
private:
printer& operator=(const printer&);
};
With the message:
/usr/local/include/boost/variant/detail/variant_io.hpp|64|error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'
Does someone know what I did wrong, and why?
Thanks a lot!
Most likely it's not finding your overload of operator <<, and then gets confused trying to match some other overload, leading to whatever message you're getting.
What you did wrong: You overloaded the stream operator in the global namespace instead of the namespace the right-hand-side class is defined in, so it's not found by ADL.
Trying to overload the stream operator for a standard class is a doomed exercise in the first place, unfortunately. You can't actually do that. I'm not sure if there is an explicit rule against it. However, if you place the operator in namespace std as you have to in order to make it properly findable by ADL, you violate the rule that you can't add your own stuff to namespace std except in very specific cases, this not being one of them.
The bottom line is that std::pair doesn't have a stream operator, and it's not possible to legally add a generic one that is useful. You can add one for a specific instantiation, if one of the parameters is a class you defined yourself; in this case the operator needs to be placed next to your own class.
Overloaded operator<< must be findable by argument dependent lookup. That means you have to put it in associated namespace of one of the arguments.
The first argument has only one associated namespace, std. The second also has only one associated namespace, std. However it is only permitted to overload symbols in std for user-defined types. Since std::pair<int, int> is not user-defined type, this is not allowed. However it is allowed for a structure or class you define yourself. Obviously in that case it is easier to place the overload to your namespace, not std.
That said if you put that overload in namespace std, it will actually work.
Also note, that boost::tuple does have operator<< (in separate header that you have to include, but it does), so you can use that instead.

Resources