2017-03-14 6 views
4

std::function을 매개 변수로 사용하는 함수가 있습니다. 그러나 전달 된 함수가 전달 된 매개 변수를 수정할 수 없도록하고 싶습니다.lddas를 std :: function으로 변환 할 때 const-correctness 적용

여기 함수의 간단한 버전입니다 (T, 그리고 일반적으로, 참조 될 수 있습니다) :

template <class T> 
void Bar(std::function<void(std::add_const_t<T>)> func) 
{ 
    // ... 
} 

잘못된 사용 :

나는 그 사용을 허용하지 않으려
Bar<int&>([](int&) { /* Do nasty stuff! */ }); /* A-OK! */ 

, 하지만이 코드는 완벽하게 잘 컴파일됩니다. (대로 안)

void Bar(std::function<void(const int&)> func) 
{ 
    // ... 
} 

그런 다음이 사용이 컴파일되지 것입니다 :

Bar([](int&) { /* Do nasty stuff! */ }); /* Error C2664 */ 

어떻게 즉, 템플릿 매개 변수를 제거하면

흥미로운 점은,이다 이것을 시행하고 템플릿 매개 변수를 유지할 수 있습니까?

+0

주'표준 : add_const_t 을'''INT &입니다. – aschepler

+0

@aschepler 필자는 문서에서도 그렇게 보았다. 그래서 나는 해킹 된 버전 인'const T'를 시도해 보았습니다.'const int '에 추론 할 것이라고 생각했습니다. 그러나. 'std :: add_const_t '을'const T'로 바꾸더라도 여전히 잘 컴파일됩니다. – Zeenobit

+0

@Zeenobit : 템플릿이 매크로가 아닙니다. 이것은'typedef int & T; T const ref;와 유사하다. 다시'ref'는'int '가 될 것이다. 또한 포인터와 비교해 보면 :'typedef int * T; T const ptr'는'int const *'가 아닌'int * const'를 얻습니다. – MSalters

답변

5

std::add_const_t<int &>int &입니다. constint에 추가하지 않았습니다. 대신을 int에 대한 참조에 추가하면 const int에 대한 참조가 아닌 int (즉 int &)의 const 참조를 얻을 수 있습니다.

간단한 방법이 될 수 있습니다 그것을 해결하기 :

#include<functional> 
#include<type_traits> 

template<typename T> 
struct to_const_ { using type = std::add_const_t<T>; }; 

template<typename T> 
struct to_const_<T &> { using type = std::add_const_t<T> &; }; 

template<typename T> 
using to_const_t = typename to_const_<T>::type; 

template <class T> 
void Bar(std::function<void(to_const_t<T>)> func) 
{} 

int main() { 
    Bar<int&>([](int&) {}); 
} 

(요청에 따라) 당신이 그것을 설정하지 않는 한 위의 코드는 컴파일되지 않습니다 작동하는지

Bar<int&>([](const int &) {}); 

주 올바른 값만 lvalue 참조와 함께,하지만 rvalue 참조 및 포인터에 대한 지원을 추가하면 아이디어가 있다면 간단합니다.


그것은 최소한의, (아마도) 작업 예를 들어 다음과 같습니다

#include<functional> 
#include<type_traits> 

template<typename T> 
struct to_const_ { using type = std::add_const_t<T>; }; 

template<typename T> 
struct to_const_<T &> { using type = std::add_const_t<T> &; }; 

template<typename T> 
struct to_const_<T &&> { using type = std::add_const_t<T> &&; }; 

template<typename T> 
struct to_const_<T * const> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type * const, std::add_const_t<typename to_const_<T>::type> * const>; }; 

template<typename T> 
struct to_const_<T *> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type *, std::add_const_t<typename to_const_<T>::type> *>; }; 

template<typename T> 
using to_const_t = typename to_const_<T>::type; 

template <class T> 
void Bar(std::function<void(to_const_t<T>)> func) 
{} 

int main() { 
    Bar<int **>([](const int **) {}); 
} 
+0

이것은 완벽합니다. 감사! – Zeenobit

0

static_assert을 사용하면 템플릿의 도움을 받아 컴파일을 실패 할 수 있습니다.

template <class T> 
void Bar(std::function<void(T)>) 
{ 
    static_assert(is_const_param<T>::value, "..."); 
} 

이가로 설정되어 T 인 경우에만 성공 :

//Base case - T is non-const, possibly pointer or reference 
template <typename T> struct is_const_param { static constexpr bool value = !std::is_lvalue_reference<T>::value && !std::is_rvalue_reference<T>::value && !std::is_pointer<T>::value; }; 

//T is const, but possibly pointer to non-const 
template <typename T> struct is_const_param<const T> { static constexpr bool value = !std::is_pointer<T>::value; }; 

//Remove reference, try again 
template <typename T> struct is_const_param<const T&> : is_const_param<const T> { }; 

//Remove reference, try again 
template <typename T> struct is_const_param<const T&&> : is_const_param<const T> { }; 

//Remove pointer, try again 
template <typename T> struct is_const_param<const T*> : is_const_param<const T> { }; 

//Remove pointer, try again 
template <typename T> struct is_const_param<const T* const> : is_const_param<const T> { }; 

static_assert은 같을 것이다 값에 의해 전달

  1. (아래 참조).
  2. 상수 참고로 전달됩니다.
  3. 포인터를 상수로 전달합니다.

성공 또는 실패의 사례를 보려면 몇 가지 테스트 사례 here을 참조하십시오.


이 값은 일반적으로 값으로 전달할 수 있도록 설정되었습니다 (예 : Bar<int>). Bar<const int> 만 허용하도록 변경하려는 경우 is_const_param<const T*> 특수화를 제거하고 특수화되지 않은 템플릿의 value 초기화를 false으로 변경하십시오. here을 볼 수 있습니다.

+0

'T **'는 어떨까요? – skypjack