The project that I have just started working on has many instances of following,
constexpr const char* str = "Some Character(s)";
I wanted to understand, is the "const" keyword in above statement not redundant, as constexpr is implicitly constant?
It is mandatory because it won't compile if you remove it. This code:
constexpr char *str = "Some Character(s)";
Produces the following error on x64 GCC 11.2 (link):
error: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
1 | constexpr char *str = "Some Character(s)";
| ^~~~~~~~~~~~~~~~~~~
The implied const is for the pointer itself so a redundant const would actually be this:
constexpr const char *const str = "Some Character(s)";
// ^~~~~
const and constexpr has diferent behaivours, as both has the same prefix, you may think that they are the same, but constexpr means that an atribute (or function, or whatever) will be done in compile time, and const means that that atribute won't be modified (it is an inmutable value, you can modify it, but thats undefined behaivour), but maybe can't be evaluated at compile time.
Also, in that particular case, you can't create an char *from an string literal since -std=c++17
Related
For the first quesion:
I want to write a function to concatenation the strings, and it can receive multiple strings;
#include <string>
#include <vector>
#include <type_traits>
template <class... Args, typename std::enable_if<std::is_same<typename std::decay<Args...>::type, std::string>::type>::type>
std::string foo(const std::string &first, const Args &... senconds) {
std::string delimiter = "$$";
std::string ret = first;
std::vector<std::string> vec{senconds...};
for (auto second = vec.rbegin(); second != vec.rend(); second++) {
ret = delimiter + *second + delimiter + ret;
}
return ret;
}
but when I invoke it like:
std::string name = "x";
name = foo(name, "xxx");
the compiler will throw an error:
error: no matching function for call to ‘foo(std::__cxx11::string&, const char [4])’
and there will be some note:
note: couldn't deduce template parameter ‘<anonymous>’
I think I should modify the constraint in the template, and I've tried all the related methods in the type_traits, but none of them works.
For the second question:
I want to hide the implementation of some function, but for the template function, it's unable to put the definition in the .hpp, and put the implementation in the .cpp, the compiler will throw a undefined reference error. Is there any elegant way to solve this?
Thanks.
There's a bit to unwrap here.
std::decay<Args...>::type can't work. std::decay takes only a single template argument, but you attempt to expand the pack here. The expansion needs to happen on the is_same.
You are also missing a way to aggregate all the is_same predicates. Do you want to and them all or or them all? Presumably and. In C++17 that's easily done with a fold expression, but for C++11 we have to work a bit.
Finally the thing the compiler complains about: std::enable_if<bla>::type evaluates to void if bla is true. That means you're formally expecting a non-type template argument, and the compiler complains because it can't deduce which value of type void it should deduce. This is normally alleviated by forming a pointer to it instead and defaulting it to nullptr: std::enable_if<bla>::type* = nullptr.
It appears (?) that you expect foo(someString, "stringLiteral"); to work. It won't, because a string literal is not a std::string. Maybe you wanted a different predicate, but for this answer I'll stick with the original condition.
Putting all that together:
In C++17, you would write
template <class... Args,
std::enable_if_t<
(std::is_same_v<std::decay_t<Args>, std::string> && ...)
>* = nullptr
>
https://godbolt.org/z/84Dcmt
In C++11, we use this helper and add back the typename and ::type verbosity:
template <class... Args,
typename std::enable_if<
var_and<
std::is_same<typename std::decay<Args>::type, std::string>::value...
>::value
>::type* = nullptr
>
https://godbolt.org/z/2eFyX7
Base on MaxLanghof's answer, I changed the template to:
template <class... Args,
typename std::enable_if<var_and<std::is_constructible<
std::string, Args>::value...>::value>::type * = nullptr>
In this form, the function foo can be invoked like the name = foo(name, stringRed, "xxx").
Thanks #MaxLanghof again.
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.
I try to write function with constexpr
constexpr QString c(const QString &columnName);
but it output issue:
enclosing class of constexpr non-static member function 'QString
DatabaseHandler::c(const QString&) const' is not a literal type
constexpr QString c(const QString &columnName);
I very bad know C++ 11 yet so cannot undestand this bug. What does it mean?
P.S. If I add static then it also output issue Invalid return type 'QString'.
constexpr functions require that their input arguments and their return type are literal types, and therefore must have at least one constexpr constructor. Qstring (i.e, the return type) does not, thus you get a compiler error.
It is not clear why I get a warning of:
[Warning] passing argument 1 of 'strlen' makes pointer from integer without a cast [enabled by default]
expected 'const char *' but argument is of type 'char'
on two of the 3 statements containing strlen() below.
Even when I attempted to cast *str it still gave the same warning.
bfr is a character buffer. *str points to that char buffer after the call to
gets(). If I use strlen(*str) I get a warning. If I use strlen(bfr) I do not.
But *str should be the equivalent to bfr. Thus the confusion regarding the error.
Now in reality, strlen arg 1 is defined as strlen(const char *string). So I
would have expected strlen(bfr) to also produce an error since bfr[] is a
char string and not a const char either.
And where is the integer that is being made into a pointer?
I am using gcc under wXDev-C++.
void test(){
FILE *fileID = fopen("somefile.txt","r");
char *str, len;
char bfr[16];
str = fgets(bfr,16,fileID); // str will be set equal to &bfr[0]
len = strlen(*str); // This gives a warning
len = strlen((const char)*str); // This gives a warning
len = strlen(bfr); // This does not give a warning
}
Sometimes you just need to take a fresh look in the morning at a problem. I realized that strlen is looking for a pointer to a string and 'str' is defined as a pointer. So *str would be a pointer to a pointer. So the warning was correct. It should read len = strlen(s) not len = strlen(*s). And it is 'str' pointing to 'bfr' not *str;
Answered my own question.
While trying to formulate a C macro to ease the writing of non-const member functions calling const member functions with exact same logic (see Chapter 1, Item 3, "Avoiding Duplication in const and Non-const Member Functions" in Effective C++), I believe I came across a decltype() bug in VS2013 Update 1.
I wanted to use decltype(*this) to build a static_cast<decltype(*this) const&>(*this) expression in the aforementioned macro to avoid having the macro call site pass any explicit type information. However, that latter expression doesn't appear to properly add const in some cases in VS2013.
Here's a small block of code I was able to make repo the bug:
#include <stdio.h>
template<typename DatumT>
struct DynamicArray
{
DatumT* elements;
unsigned element_size;
int count;
inline const DatumT* operator [](int index) const
{
if (index < 0 || index >= count)
return nullptr;
return &elements[index];
}
inline DatumT* operator [](int index)
{
#if defined(MAKE_THIS_CODE_WORK)
DynamicArray const& _this = static_cast<decltype(*this) const&>(*this);
return const_cast<DatumT*>(_this[index]);
#else
// warning C4717: 'DynamicArray<int>::operator[]' : recursive on all control paths, function will cause runtime stack overflow
return const_cast<DatumT*>(
static_cast<decltype(*this) const>(*this)
[index]
);
#endif
}
};
int _tmain(int argc, _TCHAR* argv[])
{
DynamicArray<int> array = { new int[5], sizeof(int), 5 };
printf_s("%d", *array[0]);
delete array.elements;
return 0;
}
(may the first one to blab about not using std::vector be smitten)
You can either compile the above code and see the warning yourself, or refer to my lone comment to see what VC++ would spew at you. You can then ! the defined(MAKE_THIS_CODE_WORK) expression to have VC++ compile the code as how I'm excepting the #else code to work.
I don't have my trusty clang setup on this machine, but I was able to use GCC Explorer to see if clang complains (click to see/compile code). Which it doesn't. However, g++ 4.8 will give you an ‘const’ qualifiers cannot be applied to ‘DynamicArray&’ error message using that same code. So perhaps g++ also has a bug?
Referring to the decltype and auto standards paper (albeit, it's almost 11 years old), the very bottom of page 6 says that decltype(*this) in a non-const member function should be T&, so I'm pretty sure this should be legal...
So am I wrong in trying to use decltype() on *this plus adding const to it? Or is this a bug in VS2013? And apparently g++ 4.8, but in a different manner.
edit: Thanks to Ben Voigt's response I was able to figure out how to craft a standalone C macro for what I'm desire to do.
// Cast [this] to a 'const this&' so that a const member function can be invoked
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...) \
const_cast<ret_type>( \
static_cast< \
std::add_reference< \
std::add_const< \
std::remove_reference< \
decltype(*this) \
>::type \
>::type \
>::type \
>(*this) \
__VA_ARGS__ \
)
// We can now implement that operator[] like so:
return CAST_THIS_NONCONST_MEMBER_FUNC(DatumT*, [index]);
The original desire was to hide this all in a macro, which is why I wasn't wanting to worry about creating typedefs or this aliases. It is still curious that clang in GCC Explorer didn't output a warning...though the output assembly does appear fishy.
You said yourself, decltype (*this) is T&. decltype (*this) const & tries to form a reference to a reference (T& const &). decltype triggers the reference collapsing rule 8.3.2p6. But it doesn't collapse the way you'd like.
You could say decltype(this) const&, but that would be T* const& -- a reference to a const pointer, not a pointer to a const object. For the same reason, decltype (*this) const and const decltype (*this) don't form const T&, but (T&) const. And top-level const on a reference is useless, since references already forbid rebinding.
Perhaps you are looking for something more like
const typename remove_reference<decltype(*this)>::type &
But note that you don't need the cast at all when adding const. Instead of
DynamicArray const& _this = static_cast<decltype(*this) const&>(*this);
just say
DynamicArray const& _this = *this;
These combine to
const typename std::remove_reference<decltype(*this)>::type & this_ = *this;
Still, this is a stupid amount of code for a very simple and pervasive problem. Just say:
const auto& this_ = *this;
FYI here's the text of the reference collapsing rule:
If a typedef-name (7.1.3, 14.1) or a decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a type T, an attempt to create the type "lvalue reference to cv TR" creates the type "lvalue reference to T", while an attempt to create the type "rvalue reference to cv TR" creates the type TR.
decltype(*this) is our decltype-specifier which denotes TR, which is DynamicArray<DatumT>&. Here, T is DynamicArray<DatumT>. The attempt TR const& is the first case, attempt to create lvalue reference to (const) TR, and therefore the final result is T&, not const T&. The cv-qualification is outside the innermost reference.
With regard to your macro
// Cast [this] to a 'const this&' so that a const member function can be invoked
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...) \
const_cast<ret_type>( \
static_cast< \
std::add_reference< \
std::add_const< \
std::remove_reference< \
decltype(*this) \
>::type \
>::type \
>::type \
>(*this) \
__VA_ARGS__ \
)
It's much cleaner to do
// Cast [this] to a 'const this&' so that a const member function can be invoked
template<typename T> const T& deref_as_const(T* that) { return *that; }
// [ret_type] is the return type of the member function. Usually there's a const return type, so we need to cast it to non-const too.
// [...] the code that represents the member function (or operator) call
#define CAST_THIS_NONCONST_MEMBER_FUNC(ret_type, ...) \
const_cast<ret_type>(deref_as_const(this)__VA_ARGS__)
It's shorter, self-contained, compatible with C++98 except for __VA_ARGS__, and avoids an unnecessary cast