Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does including gtest.h break template argument deduction for a std algorithm?

I upgraded to the latest release of Google Test, and several of my tests no longer compiled. I've reduced it to this:

#include <gtest/gtest.h>

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>

int main () {
    const std::string foo = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const auto uppers = std::count_if(foo.begin(), foo.end(), std::isupper);
    std::cout << "There are " << uppers << " capitals." << std::endl;
    return 0;
}

The Visual Studio 2019 (16.10.4) compiler with /std:c++latest complains:

1>Source.cpp(10,30): error C2672: 'std::count_if': no matching overloaded function found
1>Source.cpp(10,75): error C2780: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty2>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>::difference_type std::count_if(_ExPo &&,const _FwdIt,const _FwdIt,_Pr) noexcept': expects 4 arguments - 3 provided
1>algorithm(570): message : see declaration of 'std::count_if'
1>Source.cpp(10,30): error C2783: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>::difference_type std::count_if(_InIt,_InIt,_Pr)': could not deduce template argument for '_Pr'
1>algorithm(553): message : see declaration of 'std::count_if'

If I comment out the inclusion of gtest.h, the code builds and executes correctly.

What could gtest.h be doing that messes up template argument deduction for a call that depends only on std-defined types and functions?

[Note, my question is not how to workaround the problem, but to understand the specific underlying cause. I have a workaround: Replace the std::isupper with a lambda.]

like image 403
Adrian McCarthy Avatar asked Jul 23 '21 14:07

Adrian McCarthy


1 Answers

It appears that <gtest/gtest.h> is now including <locale>, which introduces

template< class charT > bool isupper( charT ch, const locale& loc ) 

into the scope. That means that std::isupper now has two possible functions it could point to and without you specifying which one to use, you get an ambiguity which causes template argument deduction to fail.

If you do go the lambda route to fix this, make sure you cast the input to std::isupper to an unsigned char like

const auto uppers = std::count_if(foo.begin(), 
                                  foo.end(), 
                                  []()(auto ch){ return std::isupper(static_cast<unsigned char>(ch));} )
like image 110
NathanOliver Avatar answered Sep 28 '22 06:09

NathanOliver