Passing a pointer as a template parameter - c++

I encounter a weird issue. In both C++03 and C++11, the following code is fine:
int someArray[] = {1,2,3,4};
template<int* ptr>
void function() {
}
int main(int argc, char* argv[]) {
function<someArray>();
return 0;
}
But if you put someArray in main, it no longer works in C++03 and C++11. Why is this? Also why does someArray need to be constexpr in main but not outside of main? Thanks in advance.

Template arguments have to be constant expressions. The address of an object that is a variable with external linkage is a constant expression; the address of a function-local object is not.

Template parameters have to be known at compile time. someArray is not know at compile time.

The problem is that definition of the language requires the array (object) that is used as a parameter of the template, to be static. Static objects have names that are know at the compile time. This name is embedded into the name of the template instantiation. This is the way how templates in C++ work.
Each instantiation of the template has its own machine code for all functions, classes, enums, etc, These classes, functions, etc. have their own names in each instantiation. These names are based on parameters. Parameter can be either a value, or reference/pointer to static const object or a template. In all three cases it should have global scope. Definition of the language requires this.
You may argue that with an additional effort compiler could understand what you are asking for. Well, it could, but the language spec does nor ask the compiler writers to do this effort.

Related

Template Class Specialization for Object Arguments

template<typename T>
class A { };
U x;
A<U> ... // OK
A<x> ... // ERROR
If we can, then how do we make a specialization of class A whose argument is an object of any type?
Before considering "argument is an object of any type", we should first understand the limitations for "argument is an object". (See also How to use an object instance as template argument?)
A clarification before getting into this: What's the difference between an argument and a parameter? A template parameter is the placeholder used in a definition, such as this question's <typename T>. An argument is the replacement provided when the definition is used, such as this question's <U>.
Objects as template parameters
Technically, a template parameter that is neither a type nor a template cannot be an object of class type, but it can be a (lvalue) reference to an object. C++20 relaxes this a bit but not all the way to "any type", so I'll ignore this caveat for now.
In practice, converting a parameter from being an object to being a reference is just a bit of syntax juggling. Still, there is an important bit of semantics to this: different objects produce different template instantiations, even if you believe the objects to be "equal". If you ignore this point, you might needlessly bloat your code. Are you sure this is consistent with your goal?
Objects as template arguments
A template argument that is neither a type nor a template must be a compile-time constant. This is a refrain that is commonly heard when discussing templates. However, it has a perhaps surprising implication when dealing with references. In order for an object reference to be a compile-time constant expression, it must refer to an object with static storage duration. That is, the object must be:
declared at namespace scope,
declared with static, or
declared with extern.
This should make sense if you think about it. A reference needs the address of the object to which it refers. Only an object that is allocated when the program begins has an address that is known at compile time. The address of an object local to a function depends on where in the call stack that particular function call lies, which is a run-time quality.
While a template parameter could be a reference to an object of any type, it is not true that any object of any type could be used as the corresponding argument.
Template parameter of any type
Subject to the above restrictions, allowing an object of any type is just a matter of allowing an object and allowing a type. There are two things that can vary. One of these can hide behind the "auto" keyword.
template<const auto & Object>
class A {};
If you want to be more restrictive about which types are accepted (such as objects of class type, as opposed to objects of fundamental type), there are various type properties that can be used with SFINAE.
Example:
template<const auto & Object>
class A {};
// A class for demonstration purposes
class U {};
// A global variable (declared at namespace scope)
U x;
int main()
{
// A static variable
static U y;
// A local variable
U z;
// Try to instantiate the template
A<x> compiles;
A<y> fine;
//A<z> fails;
// Suppress unused variable warnings
(void) compiles;
(void) fine;
(void) z;
}
Now that you know you could do this, stop to think if you should. You probably should not. Maybe it's just a bit of over-engineering? Trying to be "cool"? How bad could it get?

Why generic lambdas are allowed while nested structs with templated methods aren't?

As far as I understand - generic lambdas are transformed into objects of local scope structs with templated operator(). This makes generic lambda very powerful and easy to use tool. On the other hand one can create structs nested into the function, when however the struct has templated member e.g.:
#include <iostream>
int main() {
struct inner {
template <class T>
void operator()(T &&i) { }
};
return 0;
}
or is templated by itself:
int main() {
template <class T>
struct inner {
void operator()(T &&i) { }
};
return 0;
}
compiler seems to have a problem with compiling it:
error: invalid declaration of member template in local class
and
error: a template declaration cannot appear at block scope
I assume the problem lays more in c++ standard than in compiler bug. What are the reasons lambdas are allowed to have templated members and not the local structures?
I found this qustion, but I think the answer is kind of outdated (I don't think it's true even for c++11).
This is core issue 728, which was filed before generic lambdas were a thing.
You mentioned generic lambdas and that they were identical to local classes with corresponding member template operator(). However, they actually aren't, and the differences are related to implementation characteristics. Consider
template <typename T>
class X {
template <typename>
void foo() {
T t;
}
};
And
template <typename T>
auto bar() {
return [] (auto) {T t;};
};
Instantiating these templates with <void> will be fine in the first case, but ill-formed in the second. Why fine in the first case? foo need not be instantiatable for each particular T, but just one of them (this would be [temp.res]/(8.1)).
Why ill-formed in the second case? The generic lambda's body is instantiated - partially - using the provided template arguments. And the reason for this partial instantiation is the fact that…
…the lexical scopes used while processing a function definition are fundamentally transient, which means that delaying instantiation of some portion of a function template definition is hard to support.
(Richard Smith) We must instantiate enough of the local "template" to make it independent of the local context (which includes template parameters of the enclosing function template).
This is also related to the rationale for
[expr.prim.lambda]/13, which mandates that an entity is implicitly captured by a lambda if it…
names the entity in a potentially-evaluated expression ([basic.def.odr]) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
That is, if I have a lambda like [=] (auto x) {return (typename decltype(x)::type)a;}, where a is some block-scope variable from an enclosing function, regardless of whether x's member typedef is for void or not, the cast will cause a capture of a, because we must decide on this without waiting for an invocation of the lambda. For a discussion of this problem, see the original proposal on generic lambdas.
The bottom line is that completely postponing instantiation of a member template is not compatible with the model used by (at least one) major implementation(s), and since those are the expected semantics, the feature was not introduced.
Was that the original motivation for this constraint? It was introduced sometime between January and May 1994, with no paper covering it, so we can only get a rough idea of the prevailing notions from this paper's justification of why local classes shall not be template arguments:
Class templates and the classes generated from the template are global scope
entities and cannot refer to local scope entities.
Perhaps back then, one wanted to KISS.
I assume the problem lays more in c++ standard
Correct. This is stipulated in [temp] for class templates:
A template-declaration can appear only as a namespace scope or class scope declaration.
and [temp.mem] for member templates:
A local class of non-closure type shall not have member templates.
What are the reasons lambdas are allowed to have templated members and not the local structures?
Because once we had lambdas in C++11, it was deemed that it would be extremely useful to extend that concept to have generic lambdas. There was a proposal for such a language extension, which was revised and revised and adopted.
On the other hand, there has not yet been a proposal presented (as far as I'm aware from a brief search) that lays out the motivation for the need for member templates in local classes that isn't adequately solved by a generic lambda.
If you feel that this is an important problem that needs to be solved, feel free to submit a proposal after laying out a thoughtful motivation for why local member templates are important.

why can't apply mem_fn to member function of std::string?

struct int_holder {
int value;
int triple() {return value*3;}
};
int main(int argc, const char * argv[])
{
std::string abc{"abc"};
int_holder one{1};
auto f1 = mem_fn(&std::string::clear);
auto f2 = mem_fn(&int_holder::triple);
f1(abc);
f2(one);
}
i test such code in Xcode and the compiler issues such error
it seems mem_fn is fine with member functions of user-defined class but not with member functions of standard string, what's the different, and why?
thanks for your reading, help me plz!
I can reproduce this with Clang 3.1-3.3 as well as 3.6. Looks like bug 16478.
Simplest fix is to just use a lambda or equivalent. Other completely non-portable workarounds include either disabling extern templates with
#ifndef _LIBCPP_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...)
#endif
before you include any headers (in essence applying r189610); and doing an explicit instantiation of either the member function (template void std::string::clear();) or the entire class.
That said, you should not take the address of a member function of a standard library class. [member.functions]/p2:
An implementation may declare additional non-virtual member function
signatures within a class:
by adding arguments with default values to a member function signature;187
by replacing a member function signature with default values by two or more member function signatures with equivalent behavior; and
by adding a member function signature for a member function name.
187) Hence, the address of a member function of a class in
the C++ standard library has an unspecified type.
As for the standard, you can't take a pointer to any standard nonstatic member because the library implementation is allowed to add hidden overloads, defaulted function template parameters, SFINAE in the return type, etc.
In other words, & std::string::clear is simply not a supported operation.
In terms of Clang, it looks like an issue with hidden symbol visibility. Each shared object (linker output) file gets its own copy of certain functions to avoid the appearance that third-party shared libraries implement the standard library. With different copies floating around, the equality operator over PTMFs would not work: If you retain the value & std::string::clear from an inline function, it might not compare equal to itself later. However, it's probably a bug, since std::string should be completely implemented by the libc++.so shared library. Only other specializations of std::basic_string could really justify this behavior.
A good fix would be to use a lambda instead. []( std::string & o ) { o.clear(); } is a superior alternative to mem_fn. It avoids the indirect call and its sizeof is smaller. Really, you shouldn't use mem_fn unless absolutely necessary.

Why does declaring a "static const" member in a header file cause linker errors?

I have a class declaration(.h file) like so:
struct MyClass {
static const uint32_t SIZE = sizeof(MyType);
};
When linking my program together, I get linker errors for MyClass::SIZE. nm confirms that the symbol is undefined. http://forums.devshed.com/c-programming-42/linker-errors-undefined-reference-to-static-member-data-193010.html seems to address my problem, indicating that "class static objects must also be declared outside any function or class just like normal globals."
I have two questions:
Is this explanation valid for my case? If so, can you explain in a little more detail why this is true?
What's the best way to fix it? I'd like to keep this member's initialization entirely in the .h file.
Your problem may have nothing to do with someone taking the address of your variable. It could simply be the compiler opting not to use the variable as a constant expression even though it could. For example:
f(int const&);
struct X { enum { enum_val = 42 }; static int const static_mem = 42; };
f(5);
f(X::enum_val);
f(X::static_mem);
In the first two cases the compiler is required to use the input as a constant expression and the const& can be initialized with such. The last case however is different. Even though your intent is probably to use static_mem as a constant expression, and its completely legitimate to do so, the compiler is free to do otherwise and some will actually create a reference to the variable itself. This is "use" of the variable and thus you're required to have a definition of that variable somewhere in the program.
There's two ways to fix this issue:
1) Add a definition to your program.
2) Use enum instead:
struct X
{
enum { static_mem = ? };
};
The first solution is necessary if you ever do intend that taking the address of your variable be possible. Chances are though that you did not or you would have created that definition already. The later solution forces the compiler to use X::static_mem as a constant expression since enum members don't actually exist as objects in the program. Based on your last statement in your question, I'm betting this is the solution you really want.
The standard requires a definition for a static member integral constant only if its address is taken, otherwise the declaration with an initializer (what you have) is enough. That linker error message should mention which object/function takes an address of MyClass::SIZE.
Quoting myself from No definition available for static const member with initializer?
And from 9.4.2/4:
If a static data member is of const
integral or const enumeration type,
its declaration in the class
definition can specify a
constant initializer which shall be an
integral constant expression (5.19).
In that case, the member can appear in
integral constant expressions within
its scope. The member shall still be
defined in a namespace scope if it is
used in the program and the namespace
scope definition shall not contain an
initializer.
From these references we can infer ("...shall still be defined..." in 9.4.2/4) that if it's not defined then the program isn't well-formed.
#David Rodríguez - dribeas points out that you must be taking the address of the static member in your program somewhere. If you can avoid taking the address then there's no need for the definition in the implementation file (alternately you aren't taking the address and have a buggy compiler). Otherwise you must have the definition.
If you've to do this:
//.h file
struct MyClass
{
static const uint32_t SIZE = sizeof(MyType); //this is declaration!
};
//.cpp file
const uint32_t MyClass::SIZE; //this is definition - necessary!
Most likely, but without any error messages, it's hard to say.
Use a static const int. You can initialize it in the header, and you don't need to declare it in the cpp.

C++ closures and templates

We all know you can simulate closures in C++98 by defining local structs/classes inside a function. But is there some reason that locally defined structs can't be used to instantiate templates outside of the local scope?
For example, it would be really useful to be able to do things like this:
void work(std::vector<Foo>& foo_array)
{
struct compareFoo
{
bool operator()(const Foo& f1, const Foo& f2) const
{
return f1.bar < f2.bar;
}
};
std::sort(foo_array.begin(), foo_array.end(), compareFoo());
}
This would be especially useful if you know you're not going to need to use compareFoo anywhere else in your code. But, alas, this doesn't compile. Is there some reason that the compiler can't instantiate the std::sort template function using a locally defined struct?
There's no better reason than "it's not allowed by the standard".
I believe C++0x is going to lift this restriction, and allow you to use local classes as template parameters freely. But for now, it's not allowed.
See GOTW #58 - you can't use locally defined classes as arguments to templated types, e.g. vector wouldn't be allowed.
From the C++ standard (14.3.1/2):
A local type, a type with no linkage, an unnamed
type or a type compounded from any of these types
shall not be used as a template-argument for a
template type-parameter. [Example:
template <class T>
class X { /* ... */ };
void f()
{
struct S { /* ... */ };
X<S> x3; // error: local type used as
// template-argument
X<S*> x4; // error: pointer to local type
// used as template-argument
}
--end example]
Although I don't read this as meaning template functions like std::sort can't use a local class as an argument, apparently gcc thinks otherwise.
The local classes have no linkage (no global name), which seems like something that helps overburned compiler writers and hurts actual programmers. To actually allow a local class S to be used in vector<S> or some function<..,S>, I guess the generated thing would need a unique global name.
The way I read the standard, it prohibits using local types as template parameters in general, which would mean both class and function templates.
It says: A local type ... shall not be used as a template-argument for a template type-parameter.
The example it gives uses a class template, but I suppose there's no reason to assume that this restriction is not applicable to template functions.
Anyway, I wonder what the reason for this restriction is. It seems arbitrary.
I know that the question is a bit dated, but an easier solution is to enable the c++0x standard mode in g++ since it already supports template instantiation with locally-defined types.
g++ -std=c++0x filename.cpp -o filename