How to prevent accidental emission of constexpr functions - c++14

Ben Deane mentions the throw trick, which ensures a link error when a constexpr function is used in a non-constexpr context.
Here is my take:
#include <iostream>
struct Exc;
constexpr int foo( int a )
{
if( a == 42 )
{
throw Exc{};
}
return 666;
}
int main()
{
constexpr auto ret = foo(43);
std::cout << ret << "\n";
return ret;
}
But I couldn't make it work (clang++ 3.8.1-23 and g++ 6.3.0):
$ clang++ -std=c++14 main.cpp
main.cpp:9:15: error: invalid use of incomplete type 'ExcBase'
throw ExcBase{};
^~~~~~~~~
main.cpp:3:8: note: forward declaration of 'ExcBase'
struct ExcBase;
^
1 error generated.
A comment in this thread suggests another trick:
#include <iostream>
constexpr int foo( int a )
{
if( a == 42 )
{
(void)reinterpret_cast<int>(a);
}
return 666;
}
int main()
{
constexpr auto ret = foo(43);
std::cout << ret << "\n";
return ret;
}
Which works: no errors or warnings; when the invocation is replaced with foo(42), clang returns:
$ clang++ -std=c++14 main.cpp
main.cpp:19:20: error: constexpr variable 'ret' must be initialized by a constant expression
constexpr auto ret = foo(42);
^ ~~~~~~~
main.cpp:10:15: note: reinterpret_cast is not allowed in a constant expression
(void)reinterpret_cast<int>(a);
^
main.cpp:19:26: note: in call to 'foo(42)'
constexpr auto ret = foo(42);
^
1 error generated.
Which is great. But gcc compiles the code happily in both cases. And now the question. How can a constexpr function be written in such a way, that it cannot be used in non-constexpr environments?

The problem is you actually instantiate (call constructor) a struct which is of incomplete type. This trick you talk about requires any symbol which will not be found at link time. So instead of struct you may use int:
http://coliru.stacked-crooked.com/a/3df5207827c8888c
#include <iostream>
extern int Exc;
constexpr int foo( int a )
{
if( a == 42 )
{
throw Exc;
}
return 666;
}
int main()
{
// Compiles
constexpr auto ret = foo(43);
std::cout << ret << "\n";
// This will show linker error as expected:
// /tmp/ccQfT6hd.o: In function `main':
// main.cpp:(.text.startup+0x4c): undefined reference to `Exc'
// collect2: error: ld returned 1 exit status
int nn;
std::cin >> nn;
auto ret2 = foo(nn);
return ret;
}

Related

How to use std::chrono::milliseconds as a default parameter

Scenario
I have a C++ function which intakes a parameter as std::chrono::milliseconds. It is basically a timeout value. And, it is a default parameter set to some value by default.
Code
#include <iostream>
#include <chrono>
void Fun(const std::chrono::milliseconds someTimeout = std::chrono::milliseconds(100)) {
if (someTimeout > 0) {
std::cout << "someNumberInMillis is: " << someNumberInMillis.count() << std::endl;
}
}
int main() {
unsigned int someValue = 500;
Fun(std::chrono::milliseconds(someValue))
}
Issue
All of above is okay but, when I call Fun with a value then fails to compile and I get the following error:
No viable conversion from 'bool' to 'std::chrono::milliseconds' (aka
'duration >')
Question:
What am I doing wrong here? I want the caller of Fun to be explicitly aware that it is using std::chrono::milliseconds when it invokes Fun. But the compiler doesn't seem to allow using std::chrono::milliseconds as a parameter?
How use std::chrono::milliseconds as a default parameter?
Environment
Compiler used is clang on macOS High Sierra
With the other syntax errors fixed, this compiles without warnings in GCC 9:
#include <iostream>
#include <chrono>
void Fun(const std::chrono::milliseconds someNumberInMillis
= std::chrono::milliseconds(100))
{
if (someNumberInMillis > std::chrono::milliseconds{0}) {
std::cout << "someNumberInMillis is: " << someNumberInMillis.count()
<< std::endl;
}
}
int main()
{
unsigned int someValue = 500;
Fun(std::chrono::milliseconds(someValue));
}

How to correctly transfer the ownership of a shared_ptr?

I have the following code snipet:
// code snipet one:
#include <memory>
#include <iostream>
#include <queue>
struct A {
uint32_t val0 = 0xff;
~A() {
std::cout << "item gets freed" << std::endl;
}
};
typedef std::shared_ptr<A> A_PTR;
int main()
{
std::queue<A_PTR> Q;
Q.push(std::make_shared<A>());
auto && temp_PTR = Q.front();
std::cout << "first use count = " << temp_PTR.use_count() << std::endl;
Q.pop();
std::cout << "second use count = " << temp_PTR.use_count() <<std::endl;
return 0;
}
After running it, I got the result as following:
first use count = 1
item gets freed
second use count = 0
Q1: is anybody can explain what the type of temp_PTR after the third line of main function is called?
if I change that line as
A_PTR && temp_PTR = Q.front();
compiler complains that
main.cpp: In function 'int main()':
main.cpp:26:32: error: cannot bind '__gnu_cxx::__alloc_traits > >::value_type {aka std::shared_ptr}' lvalue to 'A_PTR&& {aka std::shared_ptr&&}'
A_PTR && temp_PTR = Q.front();
Q2: I remember that the return value of a function should be a r-value, but it seems here the compiler tell me: " hey, the return value of Queue.front() is a l-value", why is here?
For Q2, I just check the C++ docs, that the return value of Queue.front() is refernece, that means it return a l-value
reference& front();
const_reference& front() const;
For Q3, it works for A_PTR temp_PTR = std::move(Q.front());, it is what I want.

Resolve <unresolved overloaded function type> in std::async call

Can't resolve this instance of calling std::lower_bound from std::async. Example:
#include <vector>
#include <algorithm>
#include <future>
#include <iostream>
int main() {
std::vector<int> v = {1,3,4,6,7};
auto res = std::async(std::launch::async, std::lower_bound, v.begin(), v.end(), 4);
std::cout << *res.get() << std::endl;
return 0;
}
Compiler output makes it look like it doesn't realize std::launch::async is a launch policy and it should use the async call with the launch policy but I could be wrong.
test.cpp:8:84: error: no matching function for call to ‘async(std::launch, <unresolved overloaded function type>, std::vector<int>::iterator, std::vector<int>::iterator, int)’
res = std::async(std::launch::async, std::lower_bound, v.begin(), v.end(), 3);
^
In file included from test.cpp:3:0:
/usr/include/c++/6/future:1709:5: note: candidate: template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_BoundArgs>::type ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...)
async(launch __policy, _Fn&& __fn, _Args&&... __args)
^~~~~
/usr/include/c++/6/future:1709:5: note: template argument deduction/substitution failed:
test.cpp:8:84: note: couldn't deduce template parameter ‘_Fn’
res = std::async(std::launch::async, std::lower_bound, v.begin(), v.end(), 3);
^
In file included from test.cpp:3:0:
/usr/include/c++/6/future:1739:5: note: candidate: template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_BoundArgs>::type ...)>::type> std::async(_Fn&&, _Args&& ...)
async(_Fn&& __fn, _Args&&... __args)
^~~~~
/usr/include/c++/6/future:1739:5: note: template argument deduction/substitution failed:
/usr/include/c++/6/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_BoundArgs>::type ...)>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::launch; _Args = {}]’:
test.cpp:8:84: required from here
/usr/include/c++/6/future:1739:5: error: no type named ‘type’ in ‘class std::result_of<std::launch()>’
std::lower_bound is a function template, you have to explicitly specify its arguments to be able to pass it around:
int main() {
using V = std::vector<int>;
V v = {1,3,4,6,7};
auto res = std::async(std::launch::async, std::lower_bound<V::iterator, int>, v.begin(), v.end(), 4);
std::cout << *res.get() << std::endl;
return 0;
}

Issue with the templatized version of insertion sort

Below is the templatized version of insertion sort which results in compilation errors to perform insertion sort in place without any extra space.
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
template <typename T>
insertSort(T start, T end)
{
typename std::vector<typename std::iterator_traits<T>::value_type> TmpVec;
TmpVec tmp(std::make_move_iterator(start), std::make_move_iterator(end));
TmpVec::iterator begin = std::begin(tmp);
TmpVec::iterator end = std::end(tmp);
for(TmpVec::iterator i = begin; i != end; i++)
{
typename std::iterator_traits<T>::value_type value = *i;
TmpVec::iterator pos = i;
while (pos > start && *(pos-1) > value)
{
*pos = std::move(*(pos-1));
--pos;
}
*pos = value;
}
}
int main(int argc, char** argv) {
std::vector<double> arr = {1,5,3,2,6,3,9,8};
insertSort<double>(arr.begin(), arr.end());
for(int i=0; i<arr.size(); i++)
{
std::cout << arr[i] << " ";
}
return 0;
}
I am compiling this with the following compilation line.
g++ -std=c++11 -c -g -MMD -MP -MF "build/Debug/MinGW-Windows/main.o.d" -o build/Debug/MinGW-Windows/main.o main.cpp
This yields the following errors.
main.cpp: In function 'int insertSort(T, T)':
main.cpp:21:12: error: expected ';' before 'tmp'
TmpVec tmp(std::make_move_iterator(start), std::make_move_iterator(end));
^
main.cpp:22:5: error: 'TmpVec' is not a class, namespace, or enumeration
TmpVec::iterator begin = std::begin(tmp);
^
main.cpp:23:5: error: 'TmpVec' is not a class, namespace, or enumeration
TmpVec::iterator end = std::end(tmp);
^
main.cpp:24:9: error: 'TmpVec' is not a class, namespace, or enumeration
for(TmpVec::iterator i = begin; i != end; i++)
^
main.cpp:24:37: error: 'i' was not declared in this scope
for(TmpVec::iterator i = begin; i != end; i++)
^
main.cpp:27:9: error: 'TmpVec' is not a class, namespace, or enumeration
TmpVec::iterator pos = i;
^
main.cpp:28:16: error: 'pos' was not declared in this scope
while (pos > start && *(pos-1) > value)
^
main.cpp:33:10: error: 'pos' was not declared in this scope
*pos = value;
^
main.cpp: In function 'int main(int, char**)':
main.cpp:40:46: error: no matching function for call to 'insertSort(std::vector<double>::iterator, std::vector<double>::iterator)'
insertSort<double>(arr.begin(), arr.end());
^
main.cpp:40:46: note: candidate is:
main.cpp:18:1: note: template<class T> int insertSort(T, T)
insertSort(T start, T end)
^
main.cpp:18:1: note: template argument deduction/substitution failed:
main.cpp:40:46: note: cannot convert 'arr.std::vector<_Tp, _Alloc>::begin<double, std::allocator<double> >()' (type 'std::vector<double>::iterator {aka __gnu_cxx::__normal_iterator<double*, std::vector<double> >}') to type 'double'
insertSort<double>(arr.begin(), arr.end());
Kindly help in solving the above issue.
typename std::vector<typename std::iterator_traits<T>::value_type> TmpVec;
this states std::vector<typename std::iterator_traits<T>::value_type> is a typename. A compiler should issue a warning/error, because the typename is redundant.
It then declares a variable TmpVec of that type.
Every use after that uses TmpVec as if it was a type, not a variable.
You probably want
typedef std::vector<typename std::iterator_traits<T>::value_type> TmpVec;
I'd also advise
using tmp_iterator = typename TmpVec::iterator;
and using tmp_iterator as a type instead of TmpVec::iterator.

C++ range-for and boost::irange

I'm using boost::irange and created a helper function to simplify the code by removing the need for explicit template parameters. I don't understand why it doesn't work. Here's the code:
#include <iostream>
#include <boost/range/irange.hpp>
template<typename T>
boost::irange<T> range_from_zero(T limit)
{
return boost::irange<T>(T(), limit);
}
int main() {
size_t end = 100;
for (auto i : range_from_zero(0,end))
std::cout << i << ' ';
return 0;
}
There's a live version here https://ideone.com/VVvW6e, which produces compilation errors
prog.cpp:5:8: error: 'irange<T>' in namespace 'boost' does not name a type
boost::irange<T> range_from_zero(T limit)
^
prog.cpp: In function 'int main()':
prog.cpp:12:41: error: 'range_from_zero' was not declared in this scope
for (auto i : range_from_zero(0,end))
If I use boost::irange directly in the range-for, then it works:
#include <iostream>
#include <boost/range/irange.hpp>
int main() {
size_t end = 100;
for (auto i : boost::irange<size_t>(0,end))
std::cout << i << ' ';
return 0;
}
this works fine: https://ideone.com/TOWY6H
I thought maybe is was a problem using range-for on the return of a function, but it isn't; this works using a std::vector:
#include <iostream>
#include <boost/range/irange.hpp>
template<typename T>
std::vector<T> range_from_zero(T limit)
{
auto range = boost::irange<T>(T(), limit);
return { std::begin(range), std::end(range) };
}
int main() {
size_t end = 100;
for (auto i : range_from_zero(end))
std::cout << i << ' ';
return 0;
}
See https://ideone.com/TYRXnC
Any ideas, please?
But, first off, what's wrong with Live On Coliru
for (size_t i : irange(0, 100))
or even Live On Coliru
size_t end = 100;
for (auto i : irange(0ul, end))
irange is a function template, and it cannot be used as a return type.
The return type is integer_range or strided_integer_range. As such, irange is already the function you were looking for.
Only, you didn't pass arguments that could be unambiguously deduced. If you can to allow this, "copy" irange() implementation using separate template argument types for the boundary values and use e.g. std::common_type<T1,T2>::type as the range element.
Here's my stab at writing range_from_zero without naming implementation details in the interface:
Live On Coliru
#include <iostream>
#include <boost/range/irange.hpp>
template <typename T>
auto izrange(T upper) -> decltype(boost::irange(static_cast<T>(0), upper)) {
return boost::irange(static_cast<T>(0), upper);
}
int main() {
size_t end = 100;
for (size_t i : izrange(end))
std::cout << i << ' ';
}

Resources